Bug Summary

File:cafe-dictionary/libgdict/./gdict-defbox.c
Warning:line 863, column 18
Out of bound memory access (access exceeds upper limit of memory block)

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 gdict-defbox.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/cafe-dictionary/libgdict -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAVE_CONFIG_H -I . -I ../.. -D G_LOG_DOMAIN="Gdict" -D DATADIR="/usr/share" -D LIBDIR="/usr/lib" -D SYSCONFDIR="/usr/etc" -D PREFIX="/usr" -D CAFELOCALEDIR="/usr/share/locale" -D GDICTSOURCESDIR="/usr/share/cafe-dict/sources" -D GDICT_ENABLE_INTERNALS -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/ctk-3.0 -I /usr/include/pango-1.0 -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/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/gio-unix-2.0 -I /usr/include/atk-1.0 -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 -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -D G_DISABLE_CAST_CHECKS -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/13/../../../../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/cafe-dictionary/libgdict -ferror-limit 19 -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-02-07-165918-94955-1 -x c ./gdict-defbox.c
1/* gdict-defbox.c - display widget for dictionary definitions
2 *
3 * Copyright (C) 2005-2006 Emmanuele Bassi <ebassi@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 */
19
20/**
21 * SECTION:gdict-defbox
22 * @short_description: Display the list of definitions for a word
23 *
24 * The #GdictDefbox widget is a composite widget showing the list of
25 * definitions for a word. It queries the passed #GdictContext and displays
26 * the list of #GdictDefinition<!-- -->s obtained.
27 *
28 * It provides syntax highlighting, clickable links and an embedded find
29 * bar.
30 */
31
32#ifdef HAVE_CONFIG_H1
33#include "config.h"
34#endif
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <stdarg.h>
40
41#include <ctk/ctk.h>
42#include <cdk/cdkkeysyms.h>
43#include <glib/gi18n-lib.h>
44
45#include "gdict-defbox.h"
46#include "gdict-utils.h"
47#include "gdict-debug.h"
48#include "gdict-private.h"
49#include "gdict-enum-types.h"
50#include "gdict-marshal.h"
51
52#define QUERY_MARGIN48 48
53#define ERROR_MARGIN24 24
54
55typedef struct
56{
57 GdictDefinition *definition;
58
59 gint begin;
60} Definition;
61
62struct _GdictDefboxPrivate
63{
64 CtkWidget *text_view;
65
66 /* the "find" pane */
67 CtkWidget *find_pane;
68 CtkWidget *find_entry;
69 CtkWidget *find_next;
70 CtkWidget *find_prev;
71 CtkWidget *find_label;
72
73 CtkWidget *progress_dialog;
74
75 CtkTextBuffer *buffer;
76
77 GdictContext *context;
78 GSList *definitions;
79
80 gchar *word;
81 gchar *database;
82 gchar *font_name;
83
84 guint show_find : 1;
85 guint is_searching : 1;
86 guint is_hovering : 1;
87
88 CdkCursor *busy_cursor;
89 CdkCursor *hand_cursor;
90 CdkCursor *regular_cursor;
91
92 guint start_id;
93 guint end_id;
94 guint define_id;
95 guint error_id;
96 guint hide_timeout;
97
98 CtkTextTag *link_tag;
99 CtkTextTag *visited_link_tag;
100};
101
102enum
103{
104 PROP_0,
105
106 PROP_CONTEXT,
107 PROP_WORD,
108 PROP_DATABASE,
109 PROP_FONT_NAME,
110 PROP_COUNT
111};
112
113enum
114{
115 SHOW_FIND,
116 HIDE_FIND,
117 FIND_NEXT,
118 FIND_PREVIOUS,
119 LINK_CLICKED,
120
121 LAST_SIGNAL
122};
123
124static guint gdict_defbox_signals[LAST_SIGNAL] = { 0 };
125
126G_DEFINE_TYPE_WITH_PRIVATE (GdictDefbox, gdict_defbox, CTK_TYPE_BOX)static void gdict_defbox_init (GdictDefbox *self); static void
gdict_defbox_class_init (GdictDefboxClass *klass); static GType
gdict_defbox_get_type_once (void); static gpointer gdict_defbox_parent_class
= ((void*)0); static gint GdictDefbox_private_offset; static
void gdict_defbox_class_intern_init (gpointer klass) { gdict_defbox_parent_class
= g_type_class_peek_parent (klass); if (GdictDefbox_private_offset
!= 0) g_type_class_adjust_private_offset (klass, &GdictDefbox_private_offset
); gdict_defbox_class_init ((GdictDefboxClass*) klass); } __attribute__
((__unused__)) static inline gpointer gdict_defbox_get_instance_private
(GdictDefbox *self) { return (((gpointer) ((guint8*) (self) +
(glong) (GdictDefbox_private_offset)))); } GType gdict_defbox_get_type
(void) { static gsize 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 (&static_g_define_type_id
)); }))) { GType g_define_type_id = gdict_defbox_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 ((&static_g_define_type_id
), (gsize) (g_define_type_id)); })); } return static_g_define_type_id
; } __attribute__ ((__noinline__)) static GType gdict_defbox_get_type_once
(void) { GType g_define_type_id = g_type_register_static_simple
((ctk_box_get_type ()), g_intern_static_string ("GdictDefbox"
), sizeof (GdictDefboxClass), (GClassInitFunc)(void (*)(void)
) gdict_defbox_class_intern_init, sizeof (GdictDefbox), (GInstanceInitFunc
)(void (*)(void)) gdict_defbox_init, (GTypeFlags) 0); { {{ GdictDefbox_private_offset
= g_type_add_instance_private (g_define_type_id, sizeof (GdictDefboxPrivate
)); };} } return g_define_type_id; }
127
128static Definition *
129definition_new (void)
130{
131 Definition *def;
132
133 def = g_slice_new (Definition)((Definition*) g_slice_alloc (sizeof (Definition)));
134 def->definition = NULL((void*)0);
135 def->begin = -1;
136
137 return def;
138}
139
140static void
141definition_free (Definition *def)
142{
143 if (!def)
144 return;
145
146 gdict_definition_unref (def->definition);
147 g_slice_free (Definition, def)do { if (1) g_slice_free1 (sizeof (Definition), (def)); else (
void) ((Definition*) 0 == (def)); } while (0)
;
148}
149
150static void
151gdict_defbox_dispose (GObject *gobject)
152{
153 GdictDefbox *defbox = GDICT_DEFBOX (gobject)((((GdictDefbox*) (void *) ((gobject)))));
154 GdictDefboxPrivate *priv = defbox->priv;
155
156 if (priv->start_id)
157 {
158 g_signal_handler_disconnect (priv->context, priv->start_id);
159 g_signal_handler_disconnect (priv->context, priv->end_id);
160 g_signal_handler_disconnect (priv->context, priv->define_id);
161
162 priv->start_id = 0;
163 priv->end_id = 0;
164 priv->define_id = 0;
165 }
166
167 if (priv->error_id)
168 {
169 g_signal_handler_disconnect (priv->context, priv->error_id);
170 priv->error_id = 0;
171 }
172
173 if (priv->context)
174 {
175 g_object_unref (priv->context);
176 priv->context = NULL((void*)0);
177 }
178
179 if (priv->buffer)
180 {
181 g_object_unref (priv->buffer);
182 priv->buffer = NULL((void*)0);
183 }
184
185 if (priv->busy_cursor)
186 {
187 g_object_unref (priv->busy_cursor);
188 priv->busy_cursor = NULL((void*)0);
189 }
190
191 if (priv->hand_cursor)
192 {
193 g_object_unref (priv->hand_cursor);
194 priv->hand_cursor = NULL((void*)0);
195 }
196
197 if (priv->regular_cursor)
198 {
199 g_object_unref (priv->regular_cursor);
200 priv->regular_cursor = NULL((void*)0);
201 }
202
203 G_OBJECT_CLASS (gdict_defbox_parent_class)((((GObjectClass*) (void *) ((gdict_defbox_parent_class)))))->dispose (gobject);
204}
205
206static void
207gdict_defbox_finalize (GObject *object)
208{
209 GdictDefbox *defbox = GDICT_DEFBOX (object)((((GdictDefbox*) (void *) ((object)))));
210 GdictDefboxPrivate *priv = defbox->priv;
211
212 g_free (priv->database);
213 g_free (priv->word);
214 g_free (priv->font_name);
215
216 if (priv->definitions)
217 {
218 g_slist_free_full (priv->definitions, (GDestroyNotify) definition_free);
219 priv->definitions = NULL((void*)0);
220 }
221
222 G_OBJECT_CLASS (gdict_defbox_parent_class)((((GObjectClass*) (void *) ((gdict_defbox_parent_class)))))->finalize (object);
223}
224
225static void
226set_gdict_context (GdictDefbox *defbox,
227 GdictContext *context)
228{
229 GdictDefboxPrivate *priv;
230
231 g_assert (GDICT_IS_DEFBOX (defbox))do { (void) 0; } while (0);
232
233 priv = defbox->priv;
234 if (priv->context)
235 {
236 if (priv->start_id)
237 {
238 GDICT_NOTE (DEFBOX, "Removing old context handlers");
239
240 g_signal_handler_disconnect (priv->context, priv->start_id);
241 g_signal_handler_disconnect (priv->context, priv->define_id);
242 g_signal_handler_disconnect (priv->context, priv->end_id);
243
244 priv->start_id = 0;
245 priv->end_id = 0;
246 priv->define_id = 0;
247 }
248
249 if (priv->error_id)
250 {
251 g_signal_handler_disconnect (priv->context, priv->error_id);
252
253 priv->error_id = 0;
254 }
255
256 GDICT_NOTE (DEFBOX, "Removing old context");
257
258 g_object_unref (G_OBJECT (priv->context)((((GObject*) (void *) ((priv->context))))));
259 }
260
261 if (!context)
262 return;
263
264 if (!GDICT_IS_CONTEXT (context)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(context)); GType __t = ((gdict_context_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; }))))
)
265 {
266 g_warning ("Object of type '%s' instead of a GdictContext\n",
267 g_type_name (G_OBJECT_TYPE (context)(((((GTypeClass*) (((GTypeInstance*) (context))->g_class))
->g_type)))
));
268 return;
269 }
270
271 GDICT_NOTE (DEFBOX, "Setting new context");
272
273 priv->context = context;
274 g_object_ref (G_OBJECT (priv->context))((__typeof__ (((((GObject*) (void *) ((priv->context))))))
) (g_object_ref) (((((GObject*) (void *) ((priv->context))
)))))
;
275}
276
277static void
278gdict_defbox_set_property (GObject *object,
279 guint prop_id,
280 const GValue *value,
281 GParamSpec *pspec)
282{
283 GdictDefbox *defbox = GDICT_DEFBOX (object)((((GdictDefbox*) (void *) ((object)))));
284 GdictDefboxPrivate *priv = defbox->priv;
285
286 switch (prop_id)
287 {
288 case PROP_WORD:
289 gdict_defbox_lookup (defbox, g_value_get_string (value));
290 break;
291 case PROP_CONTEXT:
292 set_gdict_context (defbox, g_value_get_object (value));
293 break;
294 case PROP_DATABASE:
295 g_free (priv->database);
296 priv->database = g_strdup (g_value_get_string (value))g_strdup_inline (g_value_get_string (value));
297 break;
298 case PROP_FONT_NAME:
299 gdict_defbox_set_font_name (defbox, g_value_get_string (value));
300 break;
301 default:
302 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "./gdict-defbox.c", 302, ("property"), _glib__property_id, _glib__pspec
->name, g_type_name ((((((GTypeClass*) (((GTypeInstance*) (
_glib__pspec))->g_class))->g_type)))), (g_type_name (((
(((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
303 break;
304 }
305}
306
307static void
308gdict_defbox_get_property (GObject *object,
309 guint prop_id,
310 GValue *value,
311 GParamSpec *pspec)
312{
313 GdictDefbox *defbox = GDICT_DEFBOX (object)((((GdictDefbox*) (void *) ((object)))));
314 GdictDefboxPrivate *priv = defbox->priv;
315
316 switch (prop_id)
317 {
318 case PROP_WORD:
319 g_value_set_string (value, priv->word);
320 break;
321 case PROP_CONTEXT:
322 g_value_set_object (value, priv->context);
323 break;
324 case PROP_DATABASE:
325 g_value_set_string (value, priv->database);
326 break;
327 case PROP_FONT_NAME:
328 g_value_set_string (value, priv->font_name);
329 break;
330 default:
331 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "./gdict-defbox.c", 331, ("property"), _glib__property_id, _glib__pspec
->name, g_type_name ((((((GTypeClass*) (((GTypeInstance*) (
_glib__pspec))->g_class))->g_type)))), (g_type_name (((
(((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
332 break;
333 }
334}
335
336/*
337 * this code has been copied from ctksourceview; it's the implementation
338 * for case-insensitive search in a CtkTextBuffer. this is non-trivial, as
339 * searches on a utf-8 text stream involve a norm(casefold(norm(utf8)))
340 * operation which can be costly on large buffers. luckily for us, it's
341 * not the case on a set of definitions.
342 */
343
344#define CTK_TEXT_UNKNOWN_CHAR0xFFFC 0xFFFC
345
346/* this function acts like g_utf8_offset_to_pointer() except that if it finds a
347 * decomposable character it consumes the decomposition length from the given
348 * offset. So it's useful when the offset was calculated for the normalized
349 * version of str, but we need a pointer to str itself. */
350static const gchar *
351pointer_from_offset_skipping_decomp (const gchar *str, gint offset)
352{
353 gchar *casefold, *normal;
354 const gchar *p, *q;
355
356 p = str;
357 while (offset > 0)
358 {
359 q = g_utf8_next_char (p)(char *)((p) + g_utf8_skip[*(const guchar *)(p)]);
360 casefold = g_utf8_casefold (p, q - p);
361 normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
362 offset -= g_utf8_strlen (normal, -1);
363 g_free (casefold);
364 g_free (normal);
365 p = q;
366 }
367 return p;
368}
369
370static gboolean
371exact_prefix_cmp (const gchar *string,
372 const gchar *prefix,
373 guint prefix_len)
374{
375 GUnicodeType type;
376
377 if (strncmp (string, prefix, prefix_len) != 0)
378 return FALSE(0);
379 if (string[prefix_len] == '\0')
380 return TRUE(!(0));
381
382 type = g_unichar_type (g_utf8_get_char (string + prefix_len));
383
384 /* If string contains prefix, check that prefix is not followed
385 * by a unicode mark symbol, e.g. that trailing 'a' in prefix
386 * is not part of two-char a-with-hat symbol in string. */
387 return type != G_UNICODE_SPACING_MARK &&
388 type != G_UNICODE_ENCLOSING_MARK &&
389 type != G_UNICODE_NON_SPACING_MARK;
390}
391
392static const gchar *
393utf8_strcasestr (const gchar *haystack, const gchar *needle)
394{
395 gsize needle_len;
396 gsize haystack_len;
397 const gchar *ret = NULL((void*)0);
398 gchar *p;
399 gchar *casefold;
400 gchar *caseless_haystack;
401 gint i;
402
403 g_return_val_if_fail (haystack != NULL, NULL)do{ (void)0; }while (0);
404 g_return_val_if_fail (needle != NULL, NULL)do{ (void)0; }while (0);
405
406 casefold = g_utf8_casefold (haystack, -1);
407 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
408 g_free (casefold);
409
410 needle_len = g_utf8_strlen (needle, -1);
411 haystack_len = g_utf8_strlen (caseless_haystack, -1);
412
413 if (needle_len == 0)
414 {
415 ret = (gchar *)haystack;
416 goto finally_1;
417 }
418
419 if (haystack_len < needle_len)
420 {
421 ret = NULL((void*)0);
422 goto finally_1;
423 }
424
425 p = (gchar*)caseless_haystack;
426 needle_len = strlen (needle);
427 i = 0;
428
429 while (*p)
430 {
431 if (exact_prefix_cmp (p, needle, needle_len))
432 {
433 ret = pointer_from_offset_skipping_decomp (haystack, i);
434 goto finally_1;
435 }
436
437 p = g_utf8_next_char (p)(char *)((p) + g_utf8_skip[*(const guchar *)(p)]);
438 i++;
439 }
440
441finally_1:
442 g_free (caseless_haystack);
443
444 return ret;
445}
446
447static const gchar *
448utf8_strrcasestr (const gchar *haystack, const gchar *needle)
449{
450 gsize needle_len;
451 gsize haystack_len;
452 const gchar *ret = NULL((void*)0);
453 gchar *p;
454 gchar *casefold;
455 gchar *caseless_haystack;
456 gint i;
457
458 g_return_val_if_fail (haystack != NULL, NULL)do{ (void)0; }while (0);
459 g_return_val_if_fail (needle != NULL, NULL)do{ (void)0; }while (0);
460
461 casefold = g_utf8_casefold (haystack, -1);
462 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
463 g_free (casefold);
464
465 needle_len = g_utf8_strlen (needle, -1);
466 haystack_len = g_utf8_strlen (caseless_haystack, -1);
467
468 if (needle_len == 0)
469 {
470 ret = (gchar *)haystack;
471 goto finally_1;
472 }
473
474 if (haystack_len < needle_len)
475 {
476 ret = NULL((void*)0);
477 goto finally_1;
478 }
479
480 i = haystack_len - needle_len;
481 p = g_utf8_offset_to_pointer (caseless_haystack, i);
482 needle_len = strlen (needle);
483
484 while (p >= caseless_haystack)
485 {
486 if (exact_prefix_cmp (p, needle, needle_len))
487 {
488 ret = pointer_from_offset_skipping_decomp (haystack, i);
489 goto finally_1;
490 }
491
492 p = g_utf8_prev_char (p);
493 i--;
494 }
495
496finally_1:
497 g_free (caseless_haystack);
498
499 return ret;
500}
501
502static gboolean
503utf8_caselessnmatch (const char *s1, const char *s2,
504 gssize n1, gssize n2)
505{
506 gchar *casefold;
507 gchar *normalized_s1;
508 gchar *normalized_s2;
509 gint len_s1;
510 gint len_s2;
511 gboolean ret = FALSE(0);
512
513 g_return_val_if_fail (s1 != NULL, FALSE)do{ (void)0; }while (0);
514 g_return_val_if_fail (s2 != NULL, FALSE)do{ (void)0; }while (0);
515 g_return_val_if_fail (n1 > 0, FALSE)do{ (void)0; }while (0);
516 g_return_val_if_fail (n2 > 0, FALSE)do{ (void)0; }while (0);
517
518 casefold = g_utf8_casefold (s1, n1);
519 normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
520 g_free (casefold);
521
522 casefold = g_utf8_casefold (s2, n2);
523 normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
524 g_free (casefold);
525
526 len_s1 = strlen (normalized_s1);
527 len_s2 = strlen (normalized_s2);
528
529 if (len_s1 < len_s2)
530 goto finally_2;
531
532 ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
533
534finally_2:
535 g_free (normalized_s1);
536 g_free (normalized_s2);
537
538 return ret;
539}
540
541/* FIXME: total horror */
542static gboolean
543char_is_invisible (const CtkTextIter *iter)
544{
545 GSList *tags;
546 gboolean invisible = FALSE(0);
547 tags = ctk_text_iter_get_tags (iter);
548 while (tags)
549 {
550 gboolean this_invisible, invisible_set;
551 g_object_get (tags->data, "invisible", &this_invisible,
552 "invisible-set", &invisible_set, NULL((void*)0));
553 if (invisible_set)
554 invisible = this_invisible;
555 tags = g_slist_delete_link (tags, tags);
556 }
557 return invisible;
558}
559
560static void
561forward_chars_with_skipping (CtkTextIter *iter,
562 gint count,
563 gboolean skip_invisible,
564 gboolean skip_nontext,
565 gboolean skip_decomp)
566{
567 gint i;
568
569 g_return_if_fail (count >= 0)do{ (void)0; }while (0);
570
571 i = count;
572
573 while (i > 0)
574 {
575 gboolean ignored = FALSE(0);
576
577 /* minimal workaround to avoid the infinite loop of bug #168247.
578 * It doesn't fix the problemjust the symptom...
579 */
580 if (ctk_text_iter_is_end (iter))
581 return;
582
583 if (skip_nontext && ctk_text_iter_get_char (iter) == CTK_TEXT_UNKNOWN_CHAR0xFFFC)
584 ignored = TRUE(!(0));
585
586 /* FIXME: char_is_invisible() gets list of tags for each char there,
587 and checks every tag. It doesn't sound like a good idea. */
588 if (!ignored && skip_invisible && char_is_invisible (iter))
589 ignored = TRUE(!(0));
590
591 if (!ignored && skip_decomp)
592 {
593 /* being UTF8 correct sucks; this accounts for extra
594 offsets coming from canonical decompositions of
595 UTF8 characters (e.g. accented characters) which
596 g_utf8_normalize() performs */
597 gchar *normal;
598 gchar buffer[6];
599 gint buffer_len;
600
601 buffer_len = g_unichar_to_utf8 (ctk_text_iter_get_char (iter), buffer);
602 normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_NFD);
603 i -= (g_utf8_strlen (normal, -1) - 1);
604 g_free (normal);
605 }
606
607 ctk_text_iter_forward_char (iter);
608
609 if (!ignored)
610 --i;
611 }
612}
613
614static gboolean
615lines_match (const CtkTextIter *start,
616 const gchar **lines,
617 gboolean visible_only,
618 gboolean slice,
619 CtkTextIter *match_start,
620 CtkTextIter *match_end)
621{
622 CtkTextIter next;
623 gchar *line_text;
624 const gchar *found;
625 gint offset;
626
627 if (*lines == NULL((void*)0) || **lines == '\0')
628 {
629 if (match_start)
630 *match_start = *start;
631 if (match_end)
632 *match_end = *start;
633 return TRUE(!(0));
634 }
635
636 next = *start;
637 ctk_text_iter_forward_line (&next);
638
639 /* No more text in buffer, but *lines is nonempty */
640 if (ctk_text_iter_equal (start, &next))
641 return FALSE(0);
642
643 if (slice)
644 {
645 if (visible_only)
646 line_text = ctk_text_iter_get_visible_slice (start, &next);
647 else
648 line_text = ctk_text_iter_get_slice (start, &next);
649 }
650 else
651 {
652 if (visible_only)
653 line_text = ctk_text_iter_get_visible_text (start, &next);
654 else
655 line_text = ctk_text_iter_get_text (start, &next);
656 }
657
658 if (match_start) /* if this is the first line we're matching */
659 {
660 found = utf8_strcasestr (line_text, *lines);
661 }
662 else
663 {
664 /* If it's not the first line, we have to match from the
665 * start of the line.
666 */
667 if (utf8_caselessnmatch (line_text, *lines, strlen (line_text),
668 strlen (*lines)))
669 found = line_text;
670 else
671 found = NULL((void*)0);
672 }
673
674 if (found == NULL((void*)0))
675 {
676 g_free (line_text);
677 return FALSE(0);
678 }
679
680 /* Get offset to start of search string */
681 offset = g_utf8_strlen (line_text, found - line_text);
682
683 next = *start;
684
685 /* If match start needs to be returned, set it to the
686 * start of the search string.
687 */
688 forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE(0));
689 if (match_start)
690 {
691 *match_start = next;
692 }
693
694 /* Go to end of search string */
695 forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE(!(0)));
696
697 g_free (line_text);
698
699 ++lines;
700
701 if (match_end)
702 *match_end = next;
703
704 /* pass NULL for match_start, since we don't need to find the
705 * start again.
706 */
707 return lines_match (&next, lines, visible_only, slice, NULL((void*)0), match_end);
708}
709
710static gboolean
711backward_lines_match (const CtkTextIter *start,
712 const gchar **lines,
713 gboolean visible_only,
714 gboolean slice,
715 CtkTextIter *match_start,
716 CtkTextIter *match_end)
717{
718 CtkTextIter line, next;
719 gchar *line_text;
720 const gchar *found;
721 gint offset;
722
723 if (*lines == NULL((void*)0) || **lines == '\0')
724 {
725 if (match_start)
726 *match_start = *start;
727 if (match_end)
728 *match_end = *start;
729 return TRUE(!(0));
730 }
731
732 line = next = *start;
733 if (ctk_text_iter_get_line_offset (&next) == 0)
734 {
735 if (!ctk_text_iter_backward_line (&next))
736 return FALSE(0);
737 }
738 else
739 ctk_text_iter_set_line_offset (&next, 0);
740
741 if (slice)
742 {
743 if (visible_only)
744 line_text = ctk_text_iter_get_visible_slice (&next, &line);
745 else
746 line_text = ctk_text_iter_get_slice (&next, &line);
747 }
748 else
749 {
750 if (visible_only)
751 line_text = ctk_text_iter_get_visible_text (&next, &line);
752 else
753 line_text = ctk_text_iter_get_text (&next, &line);
754 }
755
756 if (match_start) /* if this is the first line we're matching */
757 {
758 found = utf8_strrcasestr (line_text, *lines);
759 }
760 else
761 {
762 /* If it's not the first line, we have to match from the
763 * start of the line.
764 */
765 if (utf8_caselessnmatch (line_text, *lines, strlen (line_text),
766 strlen (*lines)))
767 found = line_text;
768 else
769 found = NULL((void*)0);
770 }
771
772 if (found == NULL((void*)0))
773 {
774 g_free (line_text);
775 return FALSE(0);
776 }
777
778 /* Get offset to start of search string */
779 offset = g_utf8_strlen (line_text, found - line_text);
780
781 forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE(0));
782
783 /* If match start needs to be returned, set it to the
784 * start of the search string.
785 */
786 if (match_start)
787 {
788 *match_start = next;
789 }
790
791 /* Go to end of search string */
792 forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE(!(0)));
793
794 g_free (line_text);
795
796 ++lines;
797
798 if (match_end)
799 *match_end = next;
800
801 /* try to match the rest of the lines forward, passing NULL
802 * for match_start so lines_match will try to match the entire
803 * line */
804 return lines_match (&next, lines, visible_only,
805 slice, NULL((void*)0), match_end);
806}
807
808/* strsplit () that retains the delimiter as part of the string. */
809static gchar **
810breakup_string (const char *string,
811 const char *delimiter,
812 gint max_tokens)
813{
814 GSList *string_list = NULL((void*)0), *slist;
815 gchar **str_array, *s, *casefold, *new_string;
816 guint i, n = 1;
817
818 g_return_val_if_fail (string != NULL, NULL)do{ (void)0; }while (0);
13
Loop condition is false. Exiting loop
819 g_return_val_if_fail (delimiter != NULL, NULL)do{ (void)0; }while (0);
14
Loop condition is false. Exiting loop
820
821 if (max_tokens
14.1
'max_tokens' is < 1
< 1)
15
Taking true branch
822 max_tokens = G_MAXINT2147483647;
823
824 s = strstr (string, delimiter);
825 if (s)
16
Assuming 's' is null
17
Taking false branch
826 {
827 guint delimiter_len = strlen (delimiter);
828
829 do
830 {
831 guint len;
832
833 len = s - string + delimiter_len;
834 new_string = g_new (gchar, len + 1)((gchar *) g_malloc_n ((len + 1), sizeof (gchar)));
835 strncpy (new_string, string, len);
836 new_string[len] = 0;
837 casefold = g_utf8_casefold (new_string, -1);
838 g_free (new_string);
839 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
840 g_free (casefold);
841 string_list = g_slist_prepend (string_list, new_string);
842 n++;
843 string = s + delimiter_len;
844 s = strstr (string, delimiter);
845 } while (--max_tokens && s);
846 }
847
848 if (*string)
18
Taking true branch
849 {
850 n++;
851 casefold = g_utf8_casefold (string, -1);
852 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
853 g_free (casefold);
854 string_list = g_slist_prepend (string_list, new_string);
855 }
856
857 str_array = g_new (gchar*, n)((gchar* *) g_malloc_n ((n), sizeof (gchar*)));
858
859 i = n - 1;
860
861 str_array[i--] = NULL((void*)0);
862 for (slist = string_list; slist; slist = slist->next)
19
Loop condition is true. Entering loop body
20
Loop condition is true. Entering loop body
863 str_array[i--] = slist->data;
21
Out of bound memory access (access exceeds upper limit of memory block)
864
865 g_slist_free (string_list);
866
867 return str_array;
868}
869
870static gboolean
871gdict_defbox_iter_forward_search (const CtkTextIter *iter,
872 const gchar *str,
873 CtkTextIter *match_start,
874 CtkTextIter *match_end,
875 const CtkTextIter *limit)
876{
877 gchar **lines = NULL((void*)0);
878 CtkTextIter match;
879 gboolean retval = FALSE(0);
880 CtkTextIter search;
881
882 g_return_val_if_fail (iter != NULL, FALSE)do{ (void)0; }while (0);
9
Loop condition is false. Exiting loop
883 g_return_val_if_fail (str != NULL, FALSE)do{ (void)0; }while (0);
884
885 if (limit
9.1
'limit' is null
&& ctk_text_iter_compare (iter, limit) >= 0)
886 return FALSE(0);
887
888 if (*str == '\0')
10
Assuming the condition is false
11
Taking false branch
889 {
890 /* If we can move one char, return the empty string there */
891 match = *iter;
892
893 if (ctk_text_iter_forward_char (&match))
894 {
895 if (limit && ctk_text_iter_equal (&match, limit))
896 return FALSE(0);
897
898 if (match_start)
899 *match_start = match;
900
901 if (match_end)
902 *match_end = match;
903
904 return TRUE(!(0));
905 }
906 else
907 return FALSE(0);
908 }
909
910 /* locate all lines */
911 lines = breakup_string (str, "\n", -1);
12
Calling 'breakup_string'
912
913 search = *iter;
914
915 /* This loop has an inefficient worst-case, where
916 * ctk_text_iter_get_text () is called repeatedly on
917 * a single line.
918 */
919 do
920 {
921 CtkTextIter end;
922 gboolean res;
923
924 if (limit && ctk_text_iter_compare (&search, limit) >= 0)
925 break;
926
927 res = lines_match (&search, (const gchar**)lines,
928 TRUE(!(0)), FALSE(0),
929 &match, &end);
930 if (res)
931 {
932 if (limit == NULL((void*)0) ||
933 (limit && ctk_text_iter_compare (&end, limit) <= 0))
934 {
935 retval = TRUE(!(0));
936
937 if (match_start)
938 *match_start = match;
939
940 if (match_end)
941 *match_end = end;
942 }
943
944 break;
945 }
946 } while (ctk_text_iter_forward_line (&search));
947
948 g_strfreev ((gchar**) lines);
949
950 return retval;
951}
952
953static gboolean
954gdict_defbox_iter_backward_search (const CtkTextIter *iter,
955 const gchar *str,
956 CtkTextIter *match_start,
957 CtkTextIter *match_end,
958 const CtkTextIter *limit)
959{
960 gchar **lines = NULL((void*)0);
961 CtkTextIter match;
962 gboolean retval = FALSE(0);
963 CtkTextIter search;
964
965 g_return_val_if_fail (iter != NULL, FALSE)do{ (void)0; }while (0);
966 g_return_val_if_fail (str != NULL, FALSE)do{ (void)0; }while (0);
967
968 if (limit && ctk_text_iter_compare (iter, limit) <= 0)
969 return FALSE(0);
970
971 if (*str == '\0')
972 {
973 /* If we can move one char, return the empty string there */
974 match = *iter;
975
976 if (ctk_text_iter_backward_char (&match))
977 {
978 if (limit && ctk_text_iter_equal (&match, limit))
979 return FALSE(0);
980
981 if (match_start)
982 *match_start = match;
983
984 if (match_end)
985 *match_end = match;
986
987 return TRUE(!(0));
988 }
989 else
990 return FALSE(0);
991 }
992
993 /* locate all lines */
994 lines = breakup_string (str, "\n", -1);
995
996 search = *iter;
997
998 /* This loop has an inefficient worst-case, where
999 * ctk_text_iter_get_text () is called repeatedly on
1000 * a single line.
1001 */
1002 while (TRUE(!(0)))
1003 {
1004 CtkTextIter end;
1005 gboolean res;
1006
1007 if (limit && ctk_text_iter_compare (&search, limit) <= 0)
1008 break;
1009
1010 res = backward_lines_match (&search, (const gchar**)lines,
1011 TRUE(!(0)), FALSE(0),
1012 &match, &end);
1013 if (res)
1014 {
1015 if (limit == NULL((void*)0) ||
1016 (limit && ctk_text_iter_compare (&end, limit) > 0))
1017 {
1018 retval = TRUE(!(0));
1019
1020 if (match_start)
1021 *match_start = match;
1022
1023 if (match_end)
1024 *match_end = end;
1025
1026 }
1027
1028 break;
1029 }
1030
1031 if (ctk_text_iter_get_line_offset (&search) == 0)
1032 {
1033 if (!ctk_text_iter_backward_line (&search))
1034 break;
1035 }
1036 else
1037 ctk_text_iter_set_line_offset (&search, 0);
1038 }
1039
1040 g_strfreev ((gchar**) lines);
1041
1042 return retval;
1043}
1044
1045static gboolean
1046gdict_defbox_find_backward (GdictDefbox *defbox,
1047 const gchar *text)
1048{
1049 GdictDefboxPrivate *priv = defbox->priv;
1050 CtkTextIter start_iter, end_iter;
1051 CtkTextIter match_start, match_end;
1052 CtkTextIter iter;
1053 CtkTextMark *last_search;
1054 gboolean res;
1055
1056 g_assert (CTK_IS_TEXT_BUFFER (priv->buffer))do { (void) 0; } while (0);
1057
1058 ctk_text_buffer_get_bounds (priv->buffer, &start_iter, &end_iter);
1059
1060 /* if there already has been another result, begin from there */
1061 last_search = ctk_text_buffer_get_mark (priv->buffer, "last-search-prev");
1062 if (last_search)
1063 ctk_text_buffer_get_iter_at_mark (priv->buffer, &iter, last_search);
1064 else
1065 iter = end_iter;
1066
1067 res = gdict_defbox_iter_backward_search (&iter, text,
1068 &match_start, &match_end,
1069 NULL((void*)0));
1070 if (res)
1071 {
1072 ctk_text_view_scroll_to_iter (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))),
1073 &match_start,
1074 0.0,
1075 TRUE(!(0)),
1076 0.0, 0.0);
1077 ctk_text_buffer_place_cursor (priv->buffer, &match_end);
1078 ctk_text_buffer_move_mark (priv->buffer,
1079 ctk_text_buffer_get_mark (priv->buffer, "selection_bound"),
1080 &match_start);
1081 ctk_text_buffer_create_mark (priv->buffer, "last-search-prev", &match_start, FALSE(0));
1082 ctk_text_buffer_create_mark (priv->buffer, "last-search-next", &match_end, FALSE(0));
1083
1084 return TRUE(!(0));
1085 }
1086
1087 return FALSE(0);
1088}
1089
1090static gboolean
1091hide_find_pane (gpointer user_data)
1092{
1093 GdictDefbox *defbox = user_data;
1094
1095 ctk_widget_hide (defbox->priv->find_pane);
1096 defbox->priv->show_find = FALSE(0);
1097
1098 ctk_widget_grab_focus (defbox->priv->text_view);
1099
1100 defbox->priv->hide_timeout = 0;
1101
1102 return FALSE(0);
1103}
1104
1105static void
1106find_prev_clicked_cb (CtkWidget *widget,
1107 gpointer user_data)
1108{
1109 GdictDefbox *defbox = GDICT_DEFBOX (user_data)((((GdictDefbox*) (void *) ((user_data)))));
1110 GdictDefboxPrivate *priv = defbox->priv;
1111 const gchar *text;
1112 gboolean found;
1113
1114 ctk_widget_hide (priv->find_label);
1115
1116 text = ctk_entry_get_text (CTK_ENTRY (priv->find_entry)((((CtkEntry*) (void *) ((priv->find_entry))))));
1117 if (!text)
1118 return;
1119
1120 found = gdict_defbox_find_backward (defbox, text);
1121 if (!found)
1122 {
1123 gchar *str;
1124
1125 str = g_strconcat (" <i>", _("Not found")((char *) g_dgettext ("cafe-utils", "Not found")), "</i>", NULL((void*)0));
1126 ctk_label_set_markup (CTK_LABEL (priv->find_label)((((CtkLabel*) (void *) ((priv->find_label))))), str);
1127 ctk_widget_show (priv->find_label);
1128
1129 g_free (str);
1130 }
1131
1132 if (priv->hide_timeout)
1133 {
1134 g_source_remove (priv->hide_timeout);
1135 priv->hide_timeout = g_timeout_add_seconds (5, hide_find_pane, defbox);
1136 }
1137}
1138
1139static gboolean
1140gdict_defbox_find_forward (GdictDefbox *defbox,
1141 const gchar *text,
1142 gboolean is_typing)
1143{
1144 GdictDefboxPrivate *priv = defbox->priv;
1145 CtkTextIter start_iter, end_iter;
1146 CtkTextIter match_start, match_end;
1147 CtkTextIter iter;
1148 CtkTextMark *last_search;
1149 gboolean res;
1150
1151 g_assert (CTK_IS_TEXT_BUFFER (priv->buffer))do { (void) 0; } while (0);
4
Loop condition is false. Exiting loop
1152
1153 ctk_text_buffer_get_bounds (priv->buffer, &start_iter, &end_iter);
1154
1155 if (!is_typing
4.1
'is_typing' is 1
)
5
Taking false branch
1156 {
1157 /* if there already has been another result, begin from there */
1158 last_search = ctk_text_buffer_get_mark (priv->buffer, "last-search-next");
1159
1160 if (last_search)
1161 ctk_text_buffer_get_iter_at_mark (priv->buffer, &iter, last_search);
1162 else
1163 iter = start_iter;
1164 }
1165 else
1166 {
1167 last_search = ctk_text_buffer_get_mark (priv->buffer, "last-search-prev");
1168
1169 if (last_search)
6
Assuming 'last_search' is null
7
Taking false branch
1170 ctk_text_buffer_get_iter_at_mark (priv->buffer, &iter, last_search);
1171 else
1172 iter = start_iter;
1173 }
1174
1175 res = gdict_defbox_iter_forward_search (&iter, text,
8
Calling 'gdict_defbox_iter_forward_search'
1176 &match_start,
1177 &match_end,
1178 NULL((void*)0));
1179 if (res)
1180 {
1181 ctk_text_view_scroll_to_iter (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))),
1182 &match_start,
1183 0.0,
1184 TRUE(!(0)),
1185 0.0, 0.0);
1186 ctk_text_buffer_place_cursor (priv->buffer, &match_end);
1187 ctk_text_buffer_move_mark (priv->buffer,
1188 ctk_text_buffer_get_mark (priv->buffer, "selection_bound"),
1189 &match_start);
1190 ctk_text_buffer_create_mark (priv->buffer, "last-search-prev", &match_start, FALSE(0));
1191 ctk_text_buffer_create_mark (priv->buffer, "last-search-next", &match_end, FALSE(0));
1192
1193 return TRUE(!(0));
1194 }
1195
1196 return FALSE(0);
1197}
1198
1199static void
1200find_next_clicked_cb (CtkWidget *widget,
1201 gpointer user_data)
1202{
1203 GdictDefbox *defbox = GDICT_DEFBOX (user_data)((((GdictDefbox*) (void *) ((user_data)))));
1204 GdictDefboxPrivate *priv = defbox->priv;
1205 const gchar *text;
1206 gboolean found;
1207
1208 ctk_widget_hide (priv->find_label);
1209
1210 text = ctk_entry_get_text (CTK_ENTRY (priv->find_entry)((((CtkEntry*) (void *) ((priv->find_entry))))));
1211 if (!text)
1212 return;
1213
1214 found = gdict_defbox_find_forward (defbox, text, FALSE(0));
1215 if (!found)
1216 {
1217 gchar *str;
1218
1219 str = g_strconcat (" <i>", _("Not found")((char *) g_dgettext ("cafe-utils", "Not found")), "</i>", NULL((void*)0));
1220 ctk_label_set_markup (CTK_LABEL (priv->find_label)((((CtkLabel*) (void *) ((priv->find_label))))), str);
1221 ctk_widget_show (priv->find_label);
1222
1223 g_free (str);
1224 }
1225
1226 if (priv->hide_timeout)
1227 {
1228 g_source_remove (priv->hide_timeout);
1229 priv->hide_timeout = g_timeout_add_seconds (5, hide_find_pane, defbox);
1230 }
1231}
1232
1233static void
1234find_entry_changed_cb (CtkWidget *widget,
1235 gpointer user_data)
1236{
1237 GdictDefbox *defbox = GDICT_DEFBOX (user_data)((((GdictDefbox*) (void *) ((user_data)))));
1238 GdictDefboxPrivate *priv = defbox->priv;
1239 gchar *text;
1240 gboolean found;
1241
1242 ctk_widget_hide (priv->find_label);
1243
1244 text = ctk_editable_get_chars (CTK_EDITABLE (widget)((((CtkEditable*) (void *) ((widget))))), 0, -1);
1245 if (!text)
1
Assuming 'text' is non-null
2
Taking false branch
1246 return;
1247
1248 found = gdict_defbox_find_forward (defbox, text, TRUE(!(0)));
3
Calling 'gdict_defbox_find_forward'
1249 if (!found)
1250 {
1251 gchar *str;
1252
1253 str = g_strconcat (" <i>", _("Not found")((char *) g_dgettext ("cafe-utils", "Not found")), "</i>", NULL((void*)0));
1254 ctk_label_set_markup (CTK_LABEL (priv->find_label)((((CtkLabel*) (void *) ((priv->find_label))))), str);
1255 ctk_widget_show (priv->find_label);
1256
1257 g_free (str);
1258 }
1259
1260 g_free (text);
1261
1262 if (priv->hide_timeout)
1263 {
1264 g_source_remove (priv->hide_timeout);
1265 priv->hide_timeout = g_timeout_add_seconds (5, hide_find_pane, defbox);
1266 }
1267}
1268
1269static void
1270close_button_clicked (CtkButton *button,
1271 gpointer data)
1272{
1273 GdictDefboxPrivate *priv = GDICT_DEFBOX (data)((((GdictDefbox*) (void *) ((data)))))->priv;
1274
1275 if (priv->hide_timeout)
1276 g_source_remove (priv->hide_timeout);
1277
1278 (void) hide_find_pane (data);
1279}
1280
1281static CtkWidget *
1282create_find_pane (GdictDefbox *defbox)
1283{
1284 GdictDefboxPrivate *priv;
1285 CtkWidget *find_pane;
1286 CtkWidget *label;
1287 CtkWidget *sep;
1288 CtkWidget *hbox1, *hbox2;
1289 CtkWidget *button;
1290
1291 priv = defbox->priv;
1292
1293 find_pane = ctk_box_new (CTK_ORIENTATION_HORIZONTAL, 0);
1294 ctk_container_set_border_width (CTK_CONTAINER (find_pane)((((CtkContainer*) (void *) ((find_pane))))), 0);
1295
1296 hbox1 = ctk_box_new (CTK_ORIENTATION_HORIZONTAL, 6);
1297 ctk_box_pack_start (CTK_BOX (find_pane)((((CtkBox*) (void *) ((find_pane))))), hbox1, TRUE(!(0)), TRUE(!(0)), 0);
1298 ctk_widget_show (hbox1);
1299
1300 button = ctk_button_new ();
1301 ctk_button_set_relief (CTK_BUTTON (button)((((CtkButton*) (void *) ((button))))), CTK_RELIEF_NONE);
1302 ctk_button_set_image (CTK_BUTTON (button)((((CtkButton*) (void *) ((button))))),
1303 ctk_image_new_from_icon_name ("window-close",
1304 CTK_ICON_SIZE_BUTTON));
1305 g_signal_connect (button, "clicked",g_signal_connect_data ((button), ("clicked"), (((GCallback) (
close_button_clicked))), (defbox), ((void*)0), (GConnectFlags
) 0)
1306 G_CALLBACK (close_button_clicked), defbox)g_signal_connect_data ((button), ("clicked"), (((GCallback) (
close_button_clicked))), (defbox), ((void*)0), (GConnectFlags
) 0)
;
1307 ctk_box_pack_start (CTK_BOX (hbox1)((((CtkBox*) (void *) ((hbox1))))), button, FALSE(0), FALSE(0), 0);
1308 ctk_widget_show (button);
1309
1310 hbox2 = ctk_box_new (CTK_ORIENTATION_HORIZONTAL, 12);
1311 ctk_box_pack_start (CTK_BOX (hbox1)((((CtkBox*) (void *) ((hbox1))))), hbox2, TRUE(!(0)), TRUE(!(0)), 0);
1312 ctk_widget_show (hbox2);
1313
1314 label = ctk_label_new_with_mnemonic (_("F_ind:")((char *) g_dgettext ("cafe-utils", "F_ind:")));
1315 ctk_box_pack_start (CTK_BOX (hbox2)((((CtkBox*) (void *) ((hbox2))))), label, FALSE(0), FALSE(0), 0);
1316
1317 priv->find_entry = ctk_entry_new ();
1318 g_signal_connect (priv->find_entry, "changed",g_signal_connect_data ((priv->find_entry), ("changed"), ((
(GCallback) (find_entry_changed_cb))), (defbox), ((void*)0), (
GConnectFlags) 0)
1319 G_CALLBACK (find_entry_changed_cb), defbox)g_signal_connect_data ((priv->find_entry), ("changed"), ((
(GCallback) (find_entry_changed_cb))), (defbox), ((void*)0), (
GConnectFlags) 0)
;
1320 ctk_box_pack_start (CTK_BOX (hbox2)((((CtkBox*) (void *) ((hbox2))))), priv->find_entry, TRUE(!(0)), TRUE(!(0)), 0);
1321 ctk_label_set_mnemonic_widget (CTK_LABEL (label)((((CtkLabel*) (void *) ((label))))), priv->find_entry);
1322
1323 sep = ctk_separator_new (CTK_ORIENTATION_VERTICAL);
1324 ctk_box_pack_start (CTK_BOX (hbox1)((((CtkBox*) (void *) ((hbox1))))), sep, FALSE(0), FALSE(0), 0);
1325 ctk_widget_show (sep);
1326
1327 priv->find_prev = ctk_button_new_with_mnemonic (_("_Previous")((char *) g_dgettext ("cafe-utils", "_Previous")));
1328 ctk_button_set_image (CTK_BUTTON (priv->find_prev)((((CtkButton*) (void *) ((priv->find_prev))))),
1329 ctk_image_new_from_icon_name ("go-previous",
1330 CTK_ICON_SIZE_MENU));
1331 g_signal_connect (priv->find_prev, "clicked",g_signal_connect_data ((priv->find_prev), ("clicked"), (((
GCallback) (find_prev_clicked_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
1332 G_CALLBACK (find_prev_clicked_cb), defbox)g_signal_connect_data ((priv->find_prev), ("clicked"), (((
GCallback) (find_prev_clicked_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
;
1333 ctk_box_pack_start (CTK_BOX (hbox1)((((CtkBox*) (void *) ((hbox1))))), priv->find_prev, FALSE(0), FALSE(0), 0);
1334
1335 priv->find_next = ctk_button_new_with_mnemonic (_("_Next")((char *) g_dgettext ("cafe-utils", "_Next")));
1336 ctk_button_set_image (CTK_BUTTON (priv->find_next)((((CtkButton*) (void *) ((priv->find_next))))),
1337 ctk_image_new_from_icon_name ("go-next",
1338 CTK_ICON_SIZE_MENU));
1339 g_signal_connect (priv->find_next, "clicked",g_signal_connect_data ((priv->find_next), ("clicked"), (((
GCallback) (find_next_clicked_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
1340 G_CALLBACK (find_next_clicked_cb), defbox)g_signal_connect_data ((priv->find_next), ("clicked"), (((
GCallback) (find_next_clicked_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
;
1341 ctk_box_pack_start (CTK_BOX (hbox1)((((CtkBox*) (void *) ((hbox1))))), priv->find_next, FALSE(0), FALSE(0), 0);
1342
1343 priv->find_label = ctk_label_new (NULL((void*)0));
1344 ctk_label_set_use_markup (CTK_LABEL (priv->find_label)((((CtkLabel*) (void *) ((priv->find_label))))), TRUE(!(0)));
1345 ctk_box_pack_end (CTK_BOX (find_pane)((((CtkBox*) (void *) ((find_pane))))), priv->find_label, FALSE(0), FALSE(0), 0);
1346 ctk_widget_hide (priv->find_label);
1347
1348 return find_pane;
1349}
1350
1351static void
1352gdict_defbox_init_tags (GdictDefbox *defbox)
1353{
1354 GdictDefboxPrivate *priv = defbox->priv;
1355
1356 g_assert (CTK_IS_TEXT_BUFFER (priv->buffer))do { (void) 0; } while (0);
1357
1358 ctk_text_buffer_create_tag (priv->buffer, "italic",
1359 "style", PANGO_STYLE_ITALIC,
1360 NULL((void*)0));
1361 ctk_text_buffer_create_tag (priv->buffer, "bold",
1362 "weight", PANGO_WEIGHT_BOLD,
1363 NULL((void*)0));
1364 ctk_text_buffer_create_tag (priv->buffer, "underline",
1365 "underline", PANGO_UNDERLINE_SINGLE,
1366 NULL((void*)0));
1367
1368 ctk_text_buffer_create_tag (priv->buffer, "big",
1369 "scale", 1.6,
1370 NULL((void*)0));
1371 ctk_text_buffer_create_tag (priv->buffer, "small",
1372 "scale", PANGO_SCALE_SMALL((double)0.8333333333333),
1373 NULL((void*)0));
1374
1375 {
1376 CtkSettings *settings = ctk_widget_get_settings (CTK_WIDGET (defbox)((((CtkWidget*) (void *) ((defbox))))));
1377 gboolean prefer_dark = FALSE(0);
1378 CdkRGBA rgba;
1379
1380 /* HACK: we're hardcoding the Adwaita values because CtkTextTag
1381 * cannot be styled via CSS
1382 */
1383 g_object_get (settings, "ctk-application-prefer-dark-theme", &prefer_dark, NULL((void*)0));
1384
1385 if (!prefer_dark)
1386 cdk_rgba_parse (&rgba, "#2a76c6");
1387 else
1388 cdk_rgba_parse (&rgba, "#4a90d9");
1389
1390 priv->link_tag =
1391 ctk_text_buffer_create_tag (priv->buffer, "link",
1392 "underline", PANGO_UNDERLINE_SINGLE,
1393 "foreground-rgba", &rgba,
1394 NULL((void*)0));
1395
1396 if (!prefer_dark)
1397 cdk_rgba_parse (&rgba, "#215d9c");
1398 else
1399 cdk_rgba_parse (&rgba, "#2a76c6");
1400
1401 priv->visited_link_tag =
1402 ctk_text_buffer_create_tag (priv->buffer, "visited-link",
1403 "underline", PANGO_UNDERLINE_SINGLE,
1404 "foreground-rgba", &rgba,
1405 NULL((void*)0));
1406 }
1407
1408 ctk_text_buffer_create_tag (priv->buffer, "phonetic",
1409 "foreground", "dark gray",
1410 NULL((void*)0));
1411
1412 ctk_text_buffer_create_tag (priv->buffer, "query-title",
1413 "left-margin", QUERY_MARGIN48,
1414 "pixels-above-lines", 5,
1415 "pixels-below-lines", 20,
1416 NULL((void*)0));
1417 ctk_text_buffer_create_tag (priv->buffer, "query-from",
1418 "foreground", "dark gray",
1419 "scale", PANGO_SCALE_SMALL((double)0.8333333333333),
1420 "left-margin", QUERY_MARGIN48,
1421 "pixels-above-lines", 5,
1422 "pixels-below-lines", 10,
1423 NULL((void*)0));
1424
1425 ctk_text_buffer_create_tag (priv->buffer, "error-title",
1426 "foreground", "dark red",
1427 "left-margin", ERROR_MARGIN24,
1428 NULL((void*)0));
1429 ctk_text_buffer_create_tag (priv->buffer, "error-message",
1430 "left-margin", ERROR_MARGIN24,
1431 NULL((void*)0));
1432}
1433
1434static void
1435follow_if_is_link (GdictDefbox *defbox,
1436 CtkTextView *text_view,
1437 CtkTextIter *iter)
1438{
1439 GSList *tags, *l;
1440
1441 tags = ctk_text_iter_get_tags (iter);
1442
1443 for (l = tags; l != NULL((void*)0); l = l->next)
1444 {
1445 CtkTextTag *tag = l->data;
1446 gchar *name;
1447
1448 g_object_get (G_OBJECT (tag)((((GObject*) (void *) ((tag))))), "name", &name, NULL((void*)0));
1449 if (name &&
1450 (strcmp (name, "link") == 0 ||
1451 strcmp (name, "visited-link") == 0))
1452 {
1453 CtkTextBuffer *buffer = ctk_text_view_get_buffer (text_view);
1454 CtkTextIter start, end;
1455 gchar *link_str;
1456
1457 start = *iter;
1458 end = *iter;
1459
1460 ctk_text_iter_backward_to_tag_toggle (&start, tag);
1461 ctk_text_iter_forward_to_tag_toggle (&end, tag);
1462
1463 link_str = ctk_text_buffer_get_text (buffer, &start, &end, FALSE(0));
1464
1465 g_signal_emit (defbox, gdict_defbox_signals[LINK_CLICKED], 0, link_str);
1466
1467 g_free (link_str);
1468 g_free (name);
1469
1470 break;
1471 }
1472
1473 g_free (name);
1474 }
1475
1476 g_slist_free (tags);
1477}
1478
1479static gboolean
1480defbox_event_after_cb (CtkWidget *text_view,
1481 CdkEvent *event,
1482 GdictDefbox *defbox)
1483{
1484 CtkTextIter iter;
1485 CtkTextBuffer *buffer;
1486 CdkEventButton *button_event;
1487 gint bx, by;
1488
1489 if (event->type != CDK_BUTTON_RELEASE)
1490 return FALSE(0);
1491
1492 button_event = (CdkEventButton *) event;
1493
1494 if (button_event->button != 1)
1495 return FALSE(0);
1496
1497 buffer = ctk_text_view_get_buffer (CTK_TEXT_VIEW (text_view)((((CtkTextView*) (void *) ((text_view))))));
1498 if (ctk_text_buffer_get_has_selection (buffer))
1499 return FALSE(0);
1500
1501 ctk_text_view_window_to_buffer_coords (CTK_TEXT_VIEW (text_view)((((CtkTextView*) (void *) ((text_view))))),
1502 CTK_TEXT_WINDOW_WIDGET,
1503 button_event->x, button_event->y,
1504 &bx, &by);
1505
1506 ctk_text_view_get_iter_at_location (CTK_TEXT_VIEW (text_view)((((CtkTextView*) (void *) ((text_view))))),
1507 &iter,
1508 bx, by);
1509
1510 follow_if_is_link (defbox, CTK_TEXT_VIEW (text_view)((((CtkTextView*) (void *) ((text_view))))), &iter);
1511
1512 return FALSE(0);
1513}
1514
1515static void
1516set_cursor_if_appropriate (GdictDefbox *defbox,
1517 CtkTextView *text_view,
1518 gint x,
1519 gint y)
1520{
1521 GdictDefboxPrivate *priv;
1522 GSList *tags, *l;
1523 CtkTextIter iter;
1524 gboolean hovering = FALSE(0);
1525
1526 priv = defbox->priv;
1527
1528 if (!priv->hand_cursor)
1529 {
1530 CdkDisplay *display = ctk_widget_get_display (CTK_WIDGET (defbox)((((CtkWidget*) (void *) ((defbox))))));
1531 priv->hand_cursor = cdk_cursor_new_for_display (display, CDK_HAND2);
1532 }
1533
1534 if (!priv->regular_cursor)
1535 {
1536 CdkDisplay *display = ctk_widget_get_display (CTK_WIDGET (defbox)((((CtkWidget*) (void *) ((defbox))))));
1537 priv->regular_cursor = cdk_cursor_new_for_display (display, CDK_XTERM);
1538 }
1539
1540 ctk_text_view_get_iter_at_location (text_view, &iter, x, y);
1541
1542 tags = ctk_text_iter_get_tags (&iter);
1543 for (l = tags; l != NULL((void*)0); l = l->next)
1544 {
1545 CtkTextTag *tag = l->data;
1546 gchar *name;
1547
1548 g_object_get (G_OBJECT (tag)((((GObject*) (void *) ((tag))))), "name", &name, NULL((void*)0));
1549 if (name &&
1550 (strcmp (name, "link") == 0 ||
1551 strcmp (name, "visited-link") == 0))
1552 {
1553 hovering = TRUE(!(0));
1554 g_free (name);
1555
1556 break;
1557 }
1558
1559 g_free (name);
1560 }
1561
1562 if (hovering != defbox->priv->is_hovering)
1563 {
1564 defbox->priv->is_hovering = hovering;
1565
1566 if (defbox->priv->is_hovering)
1567 cdk_window_set_cursor (ctk_text_view_get_window (text_view,
1568 CTK_TEXT_WINDOW_TEXT),
1569 defbox->priv->hand_cursor);
1570 else
1571 cdk_window_set_cursor (ctk_text_view_get_window (text_view,
1572 CTK_TEXT_WINDOW_TEXT),
1573 defbox->priv->regular_cursor);
1574 }
1575
1576 if (tags)
1577 g_slist_free (tags);
1578}
1579
1580static gboolean
1581defbox_motion_notify_cb (CtkWidget *text_view,
1582 CdkEventMotion *event,
1583 GdictDefbox *defbox)
1584{
1585 gint bx, by;
1586
1587 ctk_text_view_window_to_buffer_coords (CTK_TEXT_VIEW (text_view)((((CtkTextView*) (void *) ((text_view))))),
1588 CTK_TEXT_WINDOW_WIDGET,
1589 event->x, event->y,
1590 &bx, &by);
1591
1592 set_cursor_if_appropriate (defbox, CTK_TEXT_VIEW (text_view)((((CtkTextView*) (void *) ((text_view))))), bx, by);
1593
1594 return FALSE(0);
1595}
1596
1597static gboolean
1598defbox_visibility_notify_cb (CtkWidget *text_view,
1599 CdkEventVisibility *event,
1600 GdictDefbox *defbox)
1601{
1602 CdkDisplay *display;
1603 CdkSeat *seat;
1604 CdkDevice *pointer;
1605 gint wx, wy;
1606 gint bx, by;
1607
1608 display = cdk_window_get_display (event->window);
1609 seat = cdk_display_get_default_seat (display);
1610 pointer = cdk_seat_get_pointer (seat);
1611 cdk_window_get_device_position (ctk_widget_get_window (text_view), pointer, &wx, &wy, NULL((void*)0));
1612
1613 ctk_text_view_window_to_buffer_coords (CTK_TEXT_VIEW (text_view)((((CtkTextView*) (void *) ((text_view))))),
1614 CTK_TEXT_WINDOW_WIDGET,
1615 wx, wy,
1616 &bx, &by);
1617
1618 set_cursor_if_appropriate (defbox, CTK_TEXT_VIEW (text_view)((((CtkTextView*) (void *) ((text_view))))), bx, by);
1619
1620 return FALSE(0);
1621}
1622
1623static GObject *
1624gdict_defbox_constructor (GType type,
1625 guint n_construct_properties,
1626 GObjectConstructParam *construct_params)
1627{
1628 GdictDefbox *defbox;
1629 GdictDefboxPrivate *priv;
1630 GObject *object;
1631 CtkWidget *sw;
1632
1633 object = G_OBJECT_CLASS (gdict_defbox_parent_class)((((GObjectClass*) (void *) ((gdict_defbox_parent_class)))))->constructor (type,
1634 n_construct_properties,
1635 construct_params);
1636 defbox = GDICT_DEFBOX (object)((((GdictDefbox*) (void *) ((object)))));
1637 priv = defbox->priv;
1638
1639 sw = ctk_scrolled_window_new (NULL((void*)0), NULL((void*)0));
1640 ctk_widget_set_vexpand (sw, TRUE(!(0)));
1641 ctk_scrolled_window_set_policy (CTK_SCROLLED_WINDOW (sw)((((CtkScrolledWindow*) (void *) ((sw))))),
1642 CTK_POLICY_AUTOMATIC,
1643 CTK_POLICY_AUTOMATIC);
1644 ctk_scrolled_window_set_shadow_type (CTK_SCROLLED_WINDOW (sw)((((CtkScrolledWindow*) (void *) ((sw))))),
1645 CTK_SHADOW_IN);
1646 ctk_box_pack_start (CTK_BOX (defbox)((((CtkBox*) (void *) ((defbox))))), sw, TRUE(!(0)), TRUE(!(0)), 0);
1647 ctk_widget_show (sw);
1648
1649 priv->buffer = ctk_text_buffer_new (NULL((void*)0));
1650 gdict_defbox_init_tags (defbox);
1651
1652 priv->text_view = ctk_text_view_new_with_buffer (priv->buffer);
1653 ctk_text_view_set_editable (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))), FALSE(0));
1654 ctk_text_view_set_left_margin (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))), 4);
1655 ctk_container_add (CTK_CONTAINER (sw)((((CtkContainer*) (void *) ((sw))))), priv->text_view);
1656 ctk_widget_show (priv->text_view);
1657
1658 priv->find_pane = create_find_pane (defbox);
1659 ctk_box_pack_end (CTK_BOX (defbox)((((CtkBox*) (void *) ((defbox))))), priv->find_pane, FALSE(0), FALSE(0), 0);
1660
1661 /* stuff to make the link machinery work */
1662 g_signal_connect (priv->text_view, "event-after",g_signal_connect_data ((priv->text_view), ("event-after"),
(((GCallback) (defbox_event_after_cb))), (defbox), ((void*)0
), (GConnectFlags) 0)
1663 G_CALLBACK (defbox_event_after_cb),g_signal_connect_data ((priv->text_view), ("event-after"),
(((GCallback) (defbox_event_after_cb))), (defbox), ((void*)0
), (GConnectFlags) 0)
1664 defbox)g_signal_connect_data ((priv->text_view), ("event-after"),
(((GCallback) (defbox_event_after_cb))), (defbox), ((void*)0
), (GConnectFlags) 0)
;
1665 g_signal_connect (priv->text_view, "motion-notify-event",g_signal_connect_data ((priv->text_view), ("motion-notify-event"
), (((GCallback) (defbox_motion_notify_cb))), (defbox), ((void
*)0), (GConnectFlags) 0)
1666 G_CALLBACK (defbox_motion_notify_cb),g_signal_connect_data ((priv->text_view), ("motion-notify-event"
), (((GCallback) (defbox_motion_notify_cb))), (defbox), ((void
*)0), (GConnectFlags) 0)
1667 defbox)g_signal_connect_data ((priv->text_view), ("motion-notify-event"
), (((GCallback) (defbox_motion_notify_cb))), (defbox), ((void
*)0), (GConnectFlags) 0)
;
1668 g_signal_connect (priv->text_view, "visibility-notify-event",g_signal_connect_data ((priv->text_view), ("visibility-notify-event"
), (((GCallback) (defbox_visibility_notify_cb))), (defbox), (
(void*)0), (GConnectFlags) 0)
1669 G_CALLBACK (defbox_visibility_notify_cb),g_signal_connect_data ((priv->text_view), ("visibility-notify-event"
), (((GCallback) (defbox_visibility_notify_cb))), (defbox), (
(void*)0), (GConnectFlags) 0)
1670 defbox)g_signal_connect_data ((priv->text_view), ("visibility-notify-event"
), (((GCallback) (defbox_visibility_notify_cb))), (defbox), (
(void*)0), (GConnectFlags) 0)
;
1671
1672 return object;
1673}
1674
1675/* we override the CtkWidget::show_all method since we have widgets
1676 * we don't want to show, such as the find pane
1677 */
1678static void
1679gdict_defbox_show_all (CtkWidget *widget)
1680{
1681 GdictDefbox *defbox = GDICT_DEFBOX (widget)((((GdictDefbox*) (void *) ((widget)))));
1682 GdictDefboxPrivate *priv = defbox->priv;
1683
1684 ctk_widget_show (widget);
1685
1686 if (priv->show_find)
1687 ctk_widget_show_all (priv->find_pane);
1688}
1689
1690static void
1691gdict_defbox_real_show_find (GdictDefbox *defbox)
1692{
1693 ctk_widget_show_all (defbox->priv->find_pane);
1694 defbox->priv->show_find = TRUE(!(0));
1695
1696 ctk_widget_grab_focus (defbox->priv->find_entry);
1697
1698 defbox->priv->hide_timeout = g_timeout_add_seconds (5, hide_find_pane, defbox);
1699}
1700
1701static void
1702gdict_defbox_real_find_next (GdictDefbox *defbox)
1703{
1704 /* synthetize a "clicked" signal to the "next" button */
1705 ctk_button_clicked (CTK_BUTTON (defbox->priv->find_next)((((CtkButton*) (void *) ((defbox->priv->find_next))))));
1706}
1707
1708static void
1709gdict_defbox_real_find_previous (GdictDefbox *defbox)
1710{
1711 /* synthetize a "clicked" signal to the "prev" button */
1712 ctk_button_clicked (CTK_BUTTON (defbox->priv->find_prev)((((CtkButton*) (void *) ((defbox->priv->find_prev))))));
1713}
1714
1715static void
1716gdict_defbox_real_hide_find (GdictDefbox *defbox)
1717{
1718 ctk_widget_hide (defbox->priv->find_pane);
1719 defbox->priv->show_find = FALSE(0);
1720
1721 ctk_widget_grab_focus (defbox->priv->text_view);
1722
1723 if (defbox->priv->hide_timeout)
1724 {
1725 g_source_remove (defbox->priv->hide_timeout);
1726 defbox->priv->hide_timeout = 0;
1727 }
1728}
1729
1730static void
1731gdict_defbox_class_init (GdictDefboxClass *klass)
1732{
1733 GObjectClass *gobject_class = G_OBJECT_CLASS (klass)((((GObjectClass*) (void *) ((klass)))));
1734 CtkWidgetClass *widget_class = CTK_WIDGET_CLASS (klass)((((CtkWidgetClass*) (void *) ((klass)))));
1735 CtkBindingSet *binding_set;
1736
1737 gobject_class->constructor = gdict_defbox_constructor;
1738 gobject_class->set_property = gdict_defbox_set_property;
1739 gobject_class->get_property = gdict_defbox_get_property;
1740 gobject_class->dispose = gdict_defbox_dispose;
1741 gobject_class->finalize = gdict_defbox_finalize;
1742
1743 widget_class->show_all = gdict_defbox_show_all;
1744
1745 /**
1746 * GdictDefbox:word:
1747 *
1748 * The word to look up.
1749 *
1750 * Since: 0.10
1751 */
1752 g_object_class_install_property (gobject_class,
1753 PROP_WORD,
1754 g_param_spec_string ("word",
1755 "Word",
1756 "The word to look up",
1757 NULL((void*)0),
1758 G_PARAM_READWRITE));
1759 /**
1760 * GdictDefbox:context:
1761 *
1762 * The #GdictContext object used to get the word definition.
1763 *
1764 * Since: 0.1
1765 */
1766 g_object_class_install_property (gobject_class,
1767 PROP_CONTEXT,
1768 g_param_spec_object ("context",
1769 "Context",
1770 "The GdictContext object used to get the word definition",
1771 GDICT_TYPE_CONTEXT(gdict_context_get_type ()),
1772 (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)));
1773 /**
1774 * GdictDefbox:database
1775 *
1776 * The database used by the #GdictDefbox bound to this object to get the word
1777 * definition.
1778 *
1779 * Since: 0.1
1780 */
1781 g_object_class_install_property (gobject_class,
1782 PROP_DATABASE,
1783 g_param_spec_string ("database",
1784 "Database",
1785 "The database used to query the GdictContext",
1786 GDICT_DEFAULT_DATABASE"*",
1787 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
1788 /**
1789 * GdictDefbox:font-name
1790 *
1791 * The name of the font used by the #GdictDefbox to display the definitions.
1792 * use the same string you use for pango_font_description_from_string().
1793 *
1794 * Since: 0.3
1795 */
1796 g_object_class_install_property (gobject_class,
1797 PROP_FONT_NAME,
1798 g_param_spec_string ("font-name",
1799 "Font Name",
1800 "The font to be used by the defbox",
1801 GDICT_DEFAULT_FONT_NAME"Sans 10",
1802 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
1803
1804 gdict_defbox_signals[SHOW_FIND] =
1805 g_signal_new ("show-find",
1806 G_OBJECT_CLASS_TYPE (gobject_class)((((GTypeClass*) (gobject_class))->g_type)),
1807 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1808 G_STRUCT_OFFSET (GdictDefboxClass, show_find)((glong) __builtin_offsetof(GdictDefboxClass, show_find)),
1809 NULL((void*)0), NULL((void*)0),
1810 gdict_marshal_VOID__VOIDg_cclosure_marshal_VOID__VOID,
1811 G_TYPE_NONE((GType) ((1) << (2))), 0);
1812 gdict_defbox_signals[FIND_PREVIOUS] =
1813 g_signal_new ("find-previous",
1814 G_OBJECT_CLASS_TYPE (gobject_class)((((GTypeClass*) (gobject_class))->g_type)),
1815 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1816 G_STRUCT_OFFSET (GdictDefboxClass, find_previous)((glong) __builtin_offsetof(GdictDefboxClass, find_previous)),
1817 NULL((void*)0), NULL((void*)0),
1818 gdict_marshal_VOID__VOIDg_cclosure_marshal_VOID__VOID,
1819 G_TYPE_NONE((GType) ((1) << (2))), 0);
1820 gdict_defbox_signals[FIND_NEXT] =
1821 g_signal_new ("find-next",
1822 G_OBJECT_CLASS_TYPE (gobject_class)((((GTypeClass*) (gobject_class))->g_type)),
1823 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1824 G_STRUCT_OFFSET (GdictDefboxClass, find_next)((glong) __builtin_offsetof(GdictDefboxClass, find_next)),
1825 NULL((void*)0), NULL((void*)0),
1826 gdict_marshal_VOID__VOIDg_cclosure_marshal_VOID__VOID,
1827 G_TYPE_NONE((GType) ((1) << (2))), 0);
1828 gdict_defbox_signals[HIDE_FIND] =
1829 g_signal_new ("hide-find",
1830 G_OBJECT_CLASS_TYPE (gobject_class)((((GTypeClass*) (gobject_class))->g_type)),
1831 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1832 G_STRUCT_OFFSET (GdictDefboxClass, hide_find)((glong) __builtin_offsetof(GdictDefboxClass, hide_find)),
1833 NULL((void*)0), NULL((void*)0),
1834 gdict_marshal_VOID__VOIDg_cclosure_marshal_VOID__VOID,
1835 G_TYPE_NONE((GType) ((1) << (2))), 0);
1836 gdict_defbox_signals[LINK_CLICKED] =
1837 g_signal_new ("link-clicked",
1838 G_OBJECT_CLASS_TYPE (gobject_class)((((GTypeClass*) (gobject_class))->g_type)),
1839 G_SIGNAL_RUN_LAST,
1840 G_STRUCT_OFFSET (GdictDefboxClass, link_clicked)((glong) __builtin_offsetof(GdictDefboxClass, link_clicked)),
1841 NULL((void*)0), NULL((void*)0),
1842 gdict_marshal_VOID__STRINGg_cclosure_marshal_VOID__STRING,
1843 G_TYPE_NONE((GType) ((1) << (2))), 1,
1844 G_TYPE_STRING((GType) ((16) << (2))));
1845
1846 klass->show_find = gdict_defbox_real_show_find;
1847 klass->hide_find = gdict_defbox_real_hide_find;
1848 klass->find_next = gdict_defbox_real_find_next;
1849 klass->find_previous = gdict_defbox_real_find_previous;
1850
1851 binding_set = ctk_binding_set_by_class (klass);
1852 ctk_binding_entry_add_signal (binding_set,
1853 CDK_KEY_f0x066, CDK_CONTROL_MASK,
1854 "show-find",
1855 0);
1856 ctk_binding_entry_add_signal (binding_set,
1857 CDK_KEY_g0x067, CDK_CONTROL_MASK,
1858 "find-next",
1859 0);
1860 ctk_binding_entry_add_signal (binding_set,
1861 CDK_KEY_g0x067, CDK_SHIFT_MASK | CDK_CONTROL_MASK,
1862 "find-previous",
1863 0);
1864 ctk_binding_entry_add_signal (binding_set,
1865 CDK_KEY_Escape0xff1b, 0,
1866 "hide-find",
1867 0);
1868}
1869
1870static void
1871gdict_defbox_init (GdictDefbox *defbox)
1872{
1873 GdictDefboxPrivate *priv;
1874
1875 ctk_orientable_set_orientation (CTK_ORIENTABLE (defbox)((((CtkOrientable*) (void *) ((defbox))))), CTK_ORIENTATION_VERTICAL);
1876 ctk_box_set_spacing (CTK_BOX (defbox)((((CtkBox*) (void *) ((defbox))))), 6);
1877
1878 priv = gdict_defbox_get_instance_private (defbox);
1879 defbox->priv = priv;
1880
1881 priv->context = NULL((void*)0);
1882 priv->database = g_strdup (GDICT_DEFAULT_DATABASE)g_strdup_inline ("*");
1883 priv->font_name = g_strdup (GDICT_DEFAULT_FONT_NAME)g_strdup_inline ("Sans 10");
1884 priv->word = NULL((void*)0);
1885
1886 priv->definitions = NULL((void*)0);
1887
1888 priv->busy_cursor = NULL((void*)0);
1889 priv->hand_cursor = NULL((void*)0);
1890 priv->regular_cursor = NULL((void*)0);
1891
1892 priv->show_find = FALSE(0);
1893 priv->is_searching = FALSE(0);
1894 priv->is_hovering = FALSE(0);
1895
1896 priv->hide_timeout = 0;
1897}
1898
1899/**
1900 * gdict_defbox_new:
1901 *
1902 * Creates a new #GdictDefbox widget. Use this widget to search for
1903 * a word using a #GdictContext, and to show the resulting definition(s).
1904 * You must set a #GdictContext for this widget using
1905 * gdict_defbox_set_context().
1906 *
1907 * Return value: a new #GdictDefbox widget.
1908 *
1909 * Since: 0.1
1910 */
1911CtkWidget *
1912gdict_defbox_new (void)
1913{
1914 return g_object_new (GDICT_TYPE_DEFBOX(gdict_defbox_get_type ()), NULL((void*)0));
1915}
1916
1917/**
1918 * gdict_defbox_new_with_context:
1919 * @context: a #GdictContext
1920 *
1921 * Creates a new #GdictDefbox widget. Use this widget to search for
1922 * a word using @context, and to show the resulting definition.
1923 *
1924 * Return value: a new #GdictDefbox widget.
1925 *
1926 * Since: 0.1
1927 */
1928CtkWidget *
1929gdict_defbox_new_with_context (GdictContext *context)
1930{
1931 g_return_val_if_fail (GDICT_IS_CONTEXT (context), NULL)do{ (void)0; }while (0);
1932
1933 return g_object_new (GDICT_TYPE_DEFBOX(gdict_defbox_get_type ()), "context", context, NULL((void*)0));
1934}
1935
1936/**
1937 * gdict_defbox_set_context:
1938 * @defbox: a #GdictDefbox
1939 * @context: a #GdictContext
1940 *
1941 * Sets @context as the #GdictContext to be used by @defbox in order
1942 * to retrieve the definitions of a word.
1943 *
1944 * Since: 0.1
1945 */
1946void
1947gdict_defbox_set_context (GdictDefbox *defbox,
1948 GdictContext *context)
1949{
1950 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
1951 g_return_if_fail (context == NULL || GDICT_IS_CONTEXT (context))do{ (void)0; }while (0);
1952
1953 g_object_set (defbox, "context", context, NULL((void*)0));
1954}
1955
1956/**
1957 * gdict_defbox_get_context:
1958 * @defbox: a #GdictDefbox
1959 *
1960 * Gets the #GdictContext used by @defbox.
1961 *
1962 * Return value: a #GdictContext.
1963 *
1964 * Since: 0.1
1965 */
1966GdictContext *
1967gdict_defbox_get_context (GdictDefbox *defbox)
1968{
1969 g_return_val_if_fail (GDICT_IS_DEFBOX (defbox), NULL)do{ (void)0; }while (0);
1970
1971 return defbox->priv->context;
1972}
1973
1974/**
1975 * gdict_defbox_set_database:
1976 * @defbox: a #GdictDefbox
1977 * @database: a database
1978 *
1979 * Sets @database as the database used by the #GdictContext bound to @defbox
1980 * to query for word definitions.
1981 *
1982 * Since: 0.1
1983 */
1984void
1985gdict_defbox_set_database (GdictDefbox *defbox,
1986 const gchar *database)
1987{
1988 GdictDefboxPrivate *priv;
1989
1990 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
1991
1992 priv = defbox->priv;
1993
1994 g_free (priv->database);
1995 priv->database = g_strdup (database)g_strdup_inline (database);
1996
1997 g_object_notify (G_OBJECT (defbox)((((GObject*) (void *) ((defbox))))), "database");
1998}
1999
2000/**
2001 * gdict_defbox_get_database:
2002 * @defbox: a #GdictDefbox
2003 *
2004 * Gets the database used by @defbox. See gdict_defbox_set_database().
2005 *
2006 * Return value: the name of a database. The return string is owned by
2007 * the #GdictDefbox widget and should not be modified or freed.
2008 *
2009 * Since: 0.1
2010 */
2011const gchar *
2012gdict_defbox_get_database (GdictDefbox *defbox)
2013{
2014 g_return_val_if_fail (GDICT_IS_DEFBOX (defbox), NULL)do{ (void)0; }while (0);
2015
2016 return defbox->priv->database;
2017}
2018
2019/**
2020 * gdict_defbox_get_word:
2021 * @defbox: a #GdictDefbox
2022 *
2023 * Retrieves the word being looked up.
2024 *
2025 * Return value: the word looked up, or %NULL. The returned string is
2026 * owned by the #GdictDefbox widget and should never be modified or
2027 * freed.
2028 *
2029 * Since: 0.12
2030 */
2031const gchar *
2032gdict_defbox_get_word (GdictDefbox *defbox)
2033{
2034 g_return_val_if_fail (GDICT_IS_DEFBOX (defbox), NULL)do{ (void)0; }while (0);
2035
2036 return defbox->priv->word;
2037}
2038
2039/**
2040 * gdict_defbox_set_show_find:
2041 * @defbox: a #GdictDefbox
2042 * @show_find: %TRUE to show the find pane
2043 *
2044 * Whether @defbox should show the find pane.
2045 *
2046 * Since: 0.1
2047 */
2048void
2049gdict_defbox_set_show_find (GdictDefbox *defbox,
2050 gboolean show_find)
2051{
2052 GdictDefboxPrivate *priv;
2053
2054 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2055
2056 priv = defbox->priv;
2057
2058 if (priv->show_find == show_find)
2059 return;
2060
2061 priv->show_find = show_find;
2062 if (priv->show_find)
2063 {
2064 ctk_widget_show_all (priv->find_pane);
2065 ctk_widget_grab_focus (priv->find_entry);
2066
2067 if (!priv->hide_timeout)
2068 priv->hide_timeout = g_timeout_add_seconds (5, hide_find_pane, defbox);
2069 }
2070 else
2071 {
2072 ctk_widget_hide (priv->find_pane);
2073
2074 if (priv->hide_timeout)
2075 {
2076 g_source_remove (priv->hide_timeout);
2077 priv->hide_timeout = 0;
2078 }
2079 }
2080}
2081
2082/**
2083 * gdict_defbox_get_show_find:
2084 * @defbox: a #GdictDefbox
2085 *
2086 * Gets whether the find pane should be visible or not.
2087 *
2088 * Return value: %TRUE if the find pane is visible.
2089 *
2090 * Since: 0.1
2091 */
2092gboolean
2093gdict_defbox_get_show_find (GdictDefbox *defbox)
2094{
2095 g_return_val_if_fail (GDICT_IS_DEFBOX (defbox), FALSE)do{ (void)0; }while (0);
2096
2097 return (defbox->priv->show_find == TRUE(!(0)));
2098}
2099
2100static void
2101lookup_start_cb (GdictContext *context,
2102 gpointer user_data)
2103{
2104 GdictDefbox *defbox = GDICT_DEFBOX (user_data)((((GdictDefbox*) (void *) ((user_data)))));
2105 GdictDefboxPrivate *priv = defbox->priv;
2106 CdkWindow *window;
2107
2108 priv->is_searching = TRUE(!(0));
2109
2110 if (!priv->busy_cursor)
2111 {
2112 CdkDisplay *display = ctk_widget_get_display (CTK_WIDGET (defbox)((((CtkWidget*) (void *) ((defbox))))));
2113 priv->busy_cursor = cdk_cursor_new_for_display (display, CDK_WATCH);
2114 }
2115
2116 window = ctk_text_view_get_window (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))),
2117 CTK_TEXT_WINDOW_WIDGET);
2118
2119 cdk_window_set_cursor (window, priv->busy_cursor);
2120}
2121
2122static void
2123lookup_end_cb (GdictContext *context,
2124 gpointer user_data)
2125{
2126 GdictDefbox *defbox = GDICT_DEFBOX (user_data)((((GdictDefbox*) (void *) ((user_data)))));
2127 GdictDefboxPrivate *priv = defbox->priv;
2128 CtkTextBuffer *buffer;
2129 CtkTextIter start;
2130 CdkWindow *window;
2131
2132 /* explicitely move the cursor to the beginning */
2133 buffer = ctk_text_view_get_buffer (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))));
2134 ctk_text_buffer_get_start_iter (buffer, &start);
2135 ctk_text_buffer_place_cursor (buffer, &start);
2136
2137 window = ctk_text_view_get_window (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))),
2138 CTK_TEXT_WINDOW_WIDGET);
2139
2140 cdk_window_set_cursor (window, NULL((void*)0));
2141
2142 priv->is_searching = FALSE(0);
2143}
2144
2145static void
2146gdict_defbox_insert_word (GdictDefbox *defbox,
2147 CtkTextIter *iter,
2148 const gchar *word)
2149{
2150 GdictDefboxPrivate *priv;
2151 gchar *text;
2152
2153 if (!word)
2154 return;
2155
2156 g_assert (GDICT_IS_DEFBOX (defbox))do { (void) 0; } while (0);
2157 priv = defbox->priv;
2158
2159 g_assert (CTK_IS_TEXT_BUFFER (priv->buffer))do { (void) 0; } while (0);
2160
2161 text = g_strdup_printf ("%s\n", word);
2162 ctk_text_buffer_insert_with_tags_by_name (priv->buffer,
2163 iter,
2164 text, strlen (text),
2165 "big", "bold", "query-title",
2166 NULL((void*)0));
2167 g_free (text);
2168}
2169
2170/* escape a link string; links are expressed as "{...}".
2171 * the link with the '{}' removed is stored inside link_str, while
2172 * the returned value is a pointer to what follows the trailing '}'.
2173 * link_str is allocated and should be freed.
2174 */
2175static const gchar *
2176escape_link (const gchar *str,
2177 gchar **link_str)
2178{
2179 gsize str_len;
2180 GString *link_buf;
2181 const gchar *p;
2182
2183 str_len = strlen (str);
2184 link_buf = g_string_sized_new (str_len - 2);
2185
2186 for (p = str + 1; *p != '}'; p++)
2187 {
2188 link_buf = g_string_append_c (link_buf, *p)g_string_append_c_inline (link_buf, *p);
2189 }
2190
2191 if (link_str)
2192 *link_str = g_string_free (link_buf, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((link_buf
), ((0))) : g_string_free_and_steal (link_buf)) : (g_string_free
) ((link_buf), ((0))))
;
2193
2194 p++;
2195
2196 return p;
2197}
2198
2199static const gchar *
2200escape_phonethic (const gchar *str,
2201 gchar **phon_str)
2202{
2203 gsize str_len;
2204 GString *phon_buf;
2205 const gchar *p;
2206
2207 str_len = strlen (str);
2208 phon_buf = g_string_sized_new (str_len - 2);
2209
2210 for (p = str + 1; *p != '\\'; p++)
2211 {
2212 phon_buf = g_string_append_c (phon_buf, *p)g_string_append_c_inline (phon_buf, *p);
2213 }
2214
2215 if (phon_str)
2216 *phon_str = g_string_free (phon_buf, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((phon_buf
), ((0))) : g_string_free_and_steal (phon_buf)) : (g_string_free
) ((phon_buf), ((0))))
;
2217
2218 p++;
2219
2220 return p;
2221}
2222
2223static void
2224gdict_defbox_insert_body (GdictDefbox *defbox,
2225 CtkTextIter *iter,
2226 const gchar *body)
2227{
2228 GdictDefboxPrivate *priv;
2229 gchar **words;
2230 gint len, i;
2231 CtkTextIter end_iter;
2232
2233 if (!body)
2234 return;
2235
2236 g_assert (GDICT_IS_DEFBOX (defbox))do { (void) 0; } while (0);
2237 priv = defbox->priv;
2238
2239 g_assert (CTK_IS_TEXT_BUFFER (priv->buffer))do { (void) 0; } while (0);
2240
2241 words = g_strsplit (body, " ", -1);
2242 len = g_strv_length (words);
2243 end_iter = *iter;
2244
2245 for (i = 0; i < len; i++)
2246 {
2247 gchar *w = words[i];
2248 gint w_len = strlen (w);
2249 gchar *begin, *end;
2250
2251 if (w_len == 0)
2252 continue;
2253
2254 begin = g_utf8_offset_to_pointer (w, 0);
2255
2256 if (*begin == '{')
2257 {
2258 end = g_utf8_strrchr (w, -1, '}');
2259
2260 /* see this is a self contained link */
2261 if (end && *end == '}')
2262 {
2263 const gchar *rest;
2264 gchar *link_str;
2265
2266 rest = escape_link (w, &link_str);
2267
2268 ctk_text_buffer_insert_with_tags_by_name (priv->buffer,
2269 &end_iter,
2270 link_str, -1,
2271 "link",
2272 NULL((void*)0));
2273
2274 ctk_text_buffer_insert (priv->buffer, &end_iter, rest, -1);
2275
2276 ctk_text_buffer_get_end_iter (priv->buffer, &end_iter);
2277 ctk_text_buffer_insert (priv->buffer, &end_iter, " ", 1);
2278
2279 g_free (link_str);
2280
2281 continue;
2282 }
2283 else
2284 {
2285 /* uh-oh: the link ends in another word */
2286 GString *buf;
2287 gchar *next;
2288 gint cur = i;
2289
2290 buf = g_string_new (NULL((void*)0));
2291 next = words[cur++];
2292
2293 while (next && (end = g_utf8_strrchr (next, -1, '}')) == NULL((void*)0))
2294 {
2295 buf = g_string_append (buf, next)(__builtin_constant_p (next) ? __extension__ ({ const char * const
__val = (next); g_string_append_len_inline (buf, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (buf, next, (gssize) -
1))
;
2296 buf = g_string_append_c (buf, ' ')g_string_append_c_inline (buf, ' ');
2297
2298 next = words[cur++];
2299 }
2300
2301 buf = g_string_append (buf, next)(__builtin_constant_p (next) ? __extension__ ({ const char * const
__val = (next); g_string_append_len_inline (buf, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (buf, next, (gssize) -
1))
;
2302
2303 next = g_string_free (buf, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((buf)
, ((0))) : g_string_free_and_steal (buf)) : (g_string_free) (
(buf), ((0))))
;
2304
2305 if (end && *end == '}')
2306 {
2307 const gchar *rest;
2308 gchar *link_str;
2309
2310 rest = escape_link (next, &link_str);
2311
2312 ctk_text_buffer_insert_with_tags_by_name (priv->buffer,
2313 &end_iter,
2314 link_str, -1,
2315 "link",
2316 NULL((void*)0));
2317
2318 ctk_text_buffer_insert (priv->buffer, &end_iter, rest, -1);
2319 ctk_text_buffer_insert (priv->buffer, &end_iter, " ", 1);
2320
2321 g_free (link_str);
2322 }
2323
2324 g_free (next);
2325 i = cur;
2326
2327 continue;
2328 }
2329 }
2330 else if (*begin == '\\')
2331 {
2332 end = g_utf8_strrchr (w, -1, '\\');
2333
2334 if (end && *end == '\\')
2335 {
2336 const gchar *rest;
2337 gchar *phon;
2338
2339 rest = escape_phonethic (w, &phon);
2340
2341 ctk_text_buffer_insert_with_tags_by_name (priv->buffer,
2342 &end_iter,
2343 phon, -1,
2344 "italic", "phonetic",
2345 NULL((void*)0));
2346
2347 ctk_text_buffer_insert (priv->buffer, &end_iter, rest, -1);
2348
2349 ctk_text_buffer_get_end_iter (priv->buffer, &end_iter);
2350 ctk_text_buffer_insert (priv->buffer, &end_iter, " ", -1);
2351
2352 g_free (phon);
2353
2354 continue;
2355 }
2356 }
2357
2358 ctk_text_buffer_insert (priv->buffer, &end_iter, w, w_len);
2359
2360 ctk_text_buffer_get_end_iter (priv->buffer, &end_iter);
2361 ctk_text_buffer_insert (priv->buffer, &end_iter, " ", 1);
2362 }
2363
2364 ctk_text_buffer_get_end_iter (priv->buffer, &end_iter);
2365 ctk_text_buffer_insert (priv->buffer, &end_iter, "\n", 1);
2366
2367 *iter = end_iter;
2368
2369 g_strfreev (words);
2370}
2371
2372static void
2373gdict_defbox_insert_from (GdictDefbox *defbox,
2374 CtkTextIter *iter,
2375 const gchar *database)
2376{
2377 GdictDefboxPrivate *priv;
2378 gchar *text;
2379
2380 if (!database)
2381 return;
2382
2383 g_assert (GDICT_IS_DEFBOX (defbox))do { (void) 0; } while (0);
2384 priv = defbox->priv;
2385
2386 g_assert (CTK_IS_TEXT_BUFFER (priv->buffer))do { (void) 0; } while (0);
2387
2388 text = g_strdup_printf ("\t-- From %s\n\n", database);
2389 ctk_text_buffer_insert_with_tags_by_name (priv->buffer,
2390 iter,
2391 text, strlen (text),
2392 "small", "query-from",
2393 NULL((void*)0));
2394 g_free (text);
2395}
2396
2397static void
2398gdict_defbox_insert_error (GdictDefbox *defbox,
2399 CtkTextIter *iter,
2400 const gchar *title,
2401 const gchar *message)
2402{
2403 GdictDefboxPrivate *priv;
2404 CtkTextMark *mark;
2405 CtkTextIter cur_iter;
2406
2407 if (!title)
2408 return;
2409
2410 g_assert (GDICT_IS_DEFBOX (defbox))do { (void) 0; } while (0);
2411 priv = defbox->priv;
2412
2413 g_assert (CTK_IS_TEXT_BUFFER (priv->buffer))do { (void) 0; } while (0);
2414
2415 mark = ctk_text_buffer_create_mark (priv->buffer, "block-cursor", iter, FALSE(0));
2416 ctk_text_buffer_get_iter_at_mark (priv->buffer, &cur_iter, mark);
2417
2418 ctk_text_buffer_insert_with_tags_by_name (priv->buffer,
2419 &cur_iter,
2420 title, strlen (title),
2421 "error-title", "big", "bold",
2422 NULL((void*)0));
2423 ctk_text_buffer_get_iter_at_mark (priv->buffer, &cur_iter, mark);
2424
2425 ctk_text_buffer_insert (priv->buffer, &cur_iter, "\n\n", -1);
2426 ctk_text_buffer_get_iter_at_mark (priv->buffer, &cur_iter, mark);
2427
2428 ctk_text_buffer_insert_with_tags_by_name (priv->buffer,
2429 &cur_iter,
2430 message, strlen (message),
2431 "error-message",
2432 NULL((void*)0));
2433}
2434
2435static void
2436definition_found_cb (GdictContext *context,
2437 GdictDefinition *definition,
2438 gpointer user_data)
2439{
2440 GdictDefbox *defbox = GDICT_DEFBOX (user_data)((((GdictDefbox*) (void *) ((user_data)))));
2441 GdictDefboxPrivate *priv = defbox->priv;
2442 CtkTextIter iter;
2443 Definition *def;
2444
2445 /* insert the word if this is the first definition */
2446 if (!priv->definitions)
2447 {
2448 ctk_text_buffer_get_start_iter (priv->buffer, &iter);
2449 gdict_defbox_insert_word (defbox, &iter,
2450 gdict_definition_get_word (definition));
2451 }
2452
2453 def = definition_new ();
2454
2455 ctk_text_buffer_get_end_iter (priv->buffer, &iter);
2456 def->begin = ctk_text_iter_get_offset (&iter);
2457 gdict_defbox_insert_body (defbox, &iter, gdict_definition_get_text (definition));
2458
2459 ctk_text_buffer_get_end_iter (priv->buffer, &iter);
2460 gdict_defbox_insert_from (defbox, &iter, gdict_definition_get_database (definition));
2461
2462 def->definition = gdict_definition_ref (definition);
2463
2464 priv->definitions = g_slist_append (priv->definitions, def);
2465}
2466
2467static void
2468error_cb (GdictContext *context,
2469 const GError *error,
2470 gpointer user_data)
2471{
2472 GdictDefbox *defbox = GDICT_DEFBOX (user_data)((((GdictDefbox*) (void *) ((user_data)))));
2473 GdictDefboxPrivate *priv = defbox->priv;
2474 CtkTextIter iter;
2475
2476 if (!error)
2477 return;
2478
2479 gdict_defbox_clear (defbox);
2480
2481 ctk_text_buffer_get_start_iter (priv->buffer, &iter);
2482 gdict_defbox_insert_error (defbox, &iter,
2483 _("Error while looking up definition")((char *) g_dgettext ("cafe-utils", "Error while looking up definition"
))
,
2484 error->message);
2485
2486 g_free (priv->word);
2487 priv->word = NULL((void*)0);
2488
2489 defbox->priv->is_searching = FALSE(0);
2490}
2491
2492/**
2493 * gdict_defbox_lookup:
2494 * @defbox: a #GdictDefbox
2495 * @word: the word to look up
2496 *
2497 * Searches @word inside the dictionary sources using the #GdictContext
2498 * provided when creating @defbox or set using gdict_defbox_set_context().
2499 *
2500 * Since: 0.1
2501 */
2502void
2503gdict_defbox_lookup (GdictDefbox *defbox,
2504 const gchar *word)
2505{
2506 GdictDefboxPrivate *priv;
2507 GError *define_error;
2508
2509 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2510
2511 priv = defbox->priv;
2512
2513 if (!priv->context)
2514 {
2515 g_warning ("Attempting to look up `%s', but no GdictContext "
2516 "has been set. Use gdict_defbox_set_context() "
2517 "before invoking gdict_defbox_lookup().",
2518 word);
2519 return;
2520 }
2521
2522 if (priv->is_searching)
2523 {
2524 _gdict_show_error_dialog (CTK_WIDGET (defbox)((((CtkWidget*) (void *) ((defbox))))),
2525 _("Another search is in progress")((char *) g_dgettext ("cafe-utils", "Another search is in progress"
))
,
2526 _("Please wait until the current search ends.")((char *) g_dgettext ("cafe-utils", "Please wait until the current search ends."
))
);
2527
2528 return;
2529 }
2530
2531 gdict_defbox_clear (defbox);
2532
2533 if (!priv->start_id)
2534 {
2535 priv->start_id = g_signal_connect (priv->context, "lookup-start",g_signal_connect_data ((priv->context), ("lookup-start"), (
((GCallback) (lookup_start_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
2536 G_CALLBACK (lookup_start_cb),g_signal_connect_data ((priv->context), ("lookup-start"), (
((GCallback) (lookup_start_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
2537 defbox)g_signal_connect_data ((priv->context), ("lookup-start"), (
((GCallback) (lookup_start_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
;
2538 priv->define_id = g_signal_connect (priv->context, "definition-found",g_signal_connect_data ((priv->context), ("definition-found"
), (((GCallback) (definition_found_cb))), (defbox), ((void*)0
), (GConnectFlags) 0)
2539 G_CALLBACK (definition_found_cb),g_signal_connect_data ((priv->context), ("definition-found"
), (((GCallback) (definition_found_cb))), (defbox), ((void*)0
), (GConnectFlags) 0)
2540 defbox)g_signal_connect_data ((priv->context), ("definition-found"
), (((GCallback) (definition_found_cb))), (defbox), ((void*)0
), (GConnectFlags) 0)
;
2541 priv->end_id = g_signal_connect (priv->context, "lookup-end",g_signal_connect_data ((priv->context), ("lookup-end"), ((
(GCallback) (lookup_end_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
2542 G_CALLBACK (lookup_end_cb),g_signal_connect_data ((priv->context), ("lookup-end"), ((
(GCallback) (lookup_end_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
2543 defbox)g_signal_connect_data ((priv->context), ("lookup-end"), ((
(GCallback) (lookup_end_cb))), (defbox), ((void*)0), (GConnectFlags
) 0)
;
2544 }
2545
2546 if (!priv->error_id)
2547 priv->error_id = g_signal_connect (priv->context, "error",g_signal_connect_data ((priv->context), ("error"), (((GCallback
) (error_cb))), (defbox), ((void*)0), (GConnectFlags) 0)
2548 G_CALLBACK (error_cb),g_signal_connect_data ((priv->context), ("error"), (((GCallback
) (error_cb))), (defbox), ((void*)0), (GConnectFlags) 0)
2549 defbox)g_signal_connect_data ((priv->context), ("error"), (((GCallback
) (error_cb))), (defbox), ((void*)0), (GConnectFlags) 0)
;
2550
2551 priv->word = g_strdup (word)g_strdup_inline (word);
2552 g_object_notify (G_OBJECT (defbox)((((GObject*) (void *) ((defbox))))), "word");
2553
2554 define_error = NULL((void*)0);
2555 gdict_context_define_word (priv->context,
2556 priv->database,
2557 word,
2558 &define_error);
2559 if (define_error)
2560 {
2561 CtkTextIter iter;
2562
2563 ctk_text_buffer_get_start_iter (priv->buffer, &iter);
2564 gdict_defbox_insert_error (defbox, &iter,
2565 _("Error while retrieving the definition")((char *) g_dgettext ("cafe-utils", "Error while retrieving the definition"
))
,
2566 define_error->message);
2567
2568 g_error_free (define_error);
2569 }
2570}
2571
2572/**
2573 * gdict_defbox_clear:
2574 * @defbox: a @GdictDefbox
2575 *
2576 * Clears the buffer of @defbox
2577 *
2578 * Since: 0.1
2579 */
2580void
2581gdict_defbox_clear (GdictDefbox *defbox)
2582{
2583 GdictDefboxPrivate *priv;
2584 CtkTextIter start, end;
2585
2586 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2587
2588 priv = defbox->priv;
2589
2590 /* destroy previously found definitions */
2591 if (priv->definitions)
2592 {
2593 g_slist_free_full (priv->definitions, (GDestroyNotify) definition_free);
2594 priv->definitions = NULL((void*)0);
2595 }
2596
2597 ctk_text_buffer_get_bounds (priv->buffer, &start, &end);
2598 ctk_text_buffer_delete (priv->buffer, &start, &end);
2599}
2600
2601/**
2602 * gdict_defbox_find_next:
2603 * @defbox: a #GdictDefbox
2604 *
2605 * Emits the "find-next" signal.
2606 *
2607 * Since: 0.1
2608 */
2609void
2610gdict_defbox_find_next (GdictDefbox *defbox)
2611{
2612 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2613
2614 g_signal_emit (defbox, gdict_defbox_signals[FIND_NEXT], 0);
2615}
2616
2617/**
2618 * gdict_defbox_find_previous:
2619 * @defbox: a #GdictDefbox
2620 *
2621 * Emits the "find-previous" signal.
2622 *
2623 * Since: 0.1
2624 */
2625void
2626gdict_defbox_find_previous (GdictDefbox *defbox)
2627{
2628 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2629
2630 g_signal_emit (defbox, gdict_defbox_signals[FIND_PREVIOUS], 0);
2631}
2632
2633/**
2634 * gdict_defbox_select_all:
2635 * @defbox: a #GdictDefbox
2636 *
2637 * Selects all the text displayed by @defbox
2638 *
2639 * Since: 0.1
2640 */
2641void
2642gdict_defbox_select_all (GdictDefbox *defbox)
2643{
2644 GdictDefboxPrivate *priv;
2645 CtkTextBuffer *buffer;
2646 CtkTextIter start, end;
2647
2648 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2649
2650 priv = defbox->priv;
2651 buffer = ctk_text_view_get_buffer (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))));
2652
2653 ctk_text_buffer_get_bounds (buffer, &start, &end);
2654 ctk_text_buffer_select_range (buffer, &start, &end);
2655}
2656
2657/**
2658 * gdict_defbox_copy_to_clipboard:
2659 * @defbox: a #GdictDefbox
2660 * @clipboard: a #CtkClipboard
2661 *
2662 * Copies the selected text inside @defbox into @clipboard.
2663 *
2664 * Since: 0.1
2665 */
2666void
2667gdict_defbox_copy_to_clipboard (GdictDefbox *defbox,
2668 CtkClipboard *clipboard)
2669{
2670 GdictDefboxPrivate *priv;
2671 CtkTextBuffer *buffer;
2672
2673 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2674 g_return_if_fail (CTK_IS_CLIPBOARD (clipboard))do{ (void)0; }while (0);
2675
2676 priv = defbox->priv;
2677 buffer = ctk_text_view_get_buffer (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))));
2678
2679 ctk_text_buffer_copy_clipboard (buffer, clipboard);
2680}
2681
2682/**
2683 * gdict_defbox_count_definitions:
2684 * @defbox: a #GdictDefbox
2685 *
2686 * Gets the number of definitions displayed by @defbox
2687 *
2688 * Return value: the number of definitions.
2689 *
2690 * Since: 0.1
2691 */
2692gint
2693gdict_defbox_count_definitions (GdictDefbox *defbox)
2694{
2695 GdictDefboxPrivate *priv;
2696
2697 g_return_val_if_fail (GDICT_IS_DEFBOX (defbox), -1)do{ (void)0; }while (0);
2698
2699 priv = defbox->priv;
2700 if (!priv->definitions)
2701 return -1;
2702
2703 return g_slist_length (priv->definitions);
2704}
2705
2706/**
2707 * gdict_defbox_jump_to_definition:
2708 * @defbox: a #GdictDefbox
2709 * @number: the definition to jump to
2710 *
2711 * Scrolls to the definition identified by @number. If @number is -1,
2712 * jumps to the last definition.
2713 *
2714 * Since: 0.1
2715 */
2716void
2717gdict_defbox_jump_to_definition (GdictDefbox *defbox,
2718 gint number)
2719{
2720 GdictDefboxPrivate *priv;
2721 gint count;
2722 Definition *def;
2723 CtkTextBuffer *buffer;
2724 CtkTextIter def_start;
2725
2726 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2727
2728 count = gdict_defbox_count_definitions (defbox) - 1;
2729 if (count == -1)
2730 return;
2731
2732 if ((number == -1) || (number > count))
2733 number = count;
2734
2735 priv = defbox->priv;
2736 def = (Definition *) g_slist_nth_data (priv->definitions, number);
2737 if (!def)
2738 return;
2739
2740 buffer = ctk_text_view_get_buffer (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))));
2741 ctk_text_buffer_get_iter_at_offset (buffer, &def_start, def->begin);
2742 ctk_text_view_scroll_to_iter (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))),
2743 &def_start,
2744 0.0,
2745 TRUE(!(0)),
2746 0.0, 0.0);
2747}
2748
2749/**
2750 * gdict_defbox_get_text:
2751 * @defbox: a #GdictDefbox
2752 * @length: return location for the text length or %NULL
2753 *
2754 * Gets the full contents of @defbox.
2755 *
2756 * Return value: a newly allocated string containing the text displayed by
2757 * @defbox.
2758 *
2759 * Since: 0.1
2760 */
2761gchar *
2762gdict_defbox_get_text (GdictDefbox *defbox,
2763 gsize *length)
2764{
2765 GdictDefboxPrivate *priv;
2766 CtkTextBuffer *buffer;
2767 CtkTextIter start, end;
2768 gchar *retval;
2769
2770 g_return_val_if_fail (GDICT_IS_DEFBOX (defbox), NULL)do{ (void)0; }while (0);
2771
2772 priv = defbox->priv;
2773 buffer = ctk_text_view_get_buffer (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))));
2774
2775 ctk_text_buffer_get_bounds (buffer, &start, &end);
2776
2777 retval = ctk_text_buffer_get_text (buffer, &start, &end, FALSE(0));
2778
2779 if (length)
2780 *length = strlen (retval);
2781
2782 return retval;
2783}
2784
2785/**
2786 * gdict_defbox_set_font_name:
2787 * @defbox: a #GdictDefbox
2788 * @font_name: a font description, or %NULL
2789 *
2790 * Sets @font_name as the font for @defbox. It calls internally
2791 * pango_font_description_from_string() and ctk_widget_modify_font().
2792 *
2793 * Passing %NULL for @font_name will reset any previously set font.
2794 *
2795 * Since: 0.3.0
2796 */
2797void
2798gdict_defbox_set_font_name (GdictDefbox *defbox,
2799 const gchar *font_name)
2800{
2801 GdictDefboxPrivate *priv;
2802 PangoFontDescription *font_desc;
2803
2804 g_return_if_fail (GDICT_IS_DEFBOX (defbox))do{ (void)0; }while (0);
2805
2806 priv = defbox->priv;
2807
2808 if (font_name)
2809 {
2810 font_desc = pango_font_description_from_string (font_name);
2811 g_return_if_fail (font_desc != NULL)do{ (void)0; }while (0);
2812 }
2813 else
2814 font_desc = NULL((void*)0);
2815
2816 ctk_widget_override_font (priv->text_view, font_desc);
2817
2818 if (font_desc)
2819 pango_font_description_free (font_desc);
2820
2821 g_free (priv->font_name);
2822 priv->font_name = g_strdup (font_name)g_strdup_inline (font_name);
2823}
2824
2825/**
2826 * gdict_defbox_get_font_name:
2827 * @defbox: a #GdictDefbox
2828 *
2829 * Retrieves the font currently used by @defbox.
2830 *
2831 * Return value: a font name. The returned string is owned by @defbox and
2832 * should not be modified or freed.
2833 *
2834 * Since: 0.3
2835 */
2836const gchar *
2837gdict_defbox_get_font_name (GdictDefbox *defbox)
2838{
2839 g_return_val_if_fail (GDICT_IS_DEFBOX (defbox), NULL)do{ (void)0; }while (0);
2840
2841 return defbox->priv->font_name;
2842}
2843
2844/**
2845 * gdict_defbox_get_selected_word:
2846 * @defbox: a #GdictDefbox
2847 *
2848 * Retrieves the selected word from the defbox widget
2849 *
2850 * Return value: a newly allocated string containing the selected
2851 * word. Use g_free() when done using it.
2852 *
2853 * Since: 0.12
2854 */
2855gchar *
2856gdict_defbox_get_selected_word (GdictDefbox *defbox)
2857{
2858 GdictDefboxPrivate *priv;
2859 CtkTextBuffer *buffer;
2860
2861 g_return_val_if_fail (GDICT_IS_DEFBOX (defbox), NULL)do{ (void)0; }while (0);
2862
2863 priv = defbox->priv;
2864 buffer = ctk_text_view_get_buffer (CTK_TEXT_VIEW (priv->text_view)((((CtkTextView*) (void *) ((priv->text_view))))));
2865
2866 if (!ctk_text_buffer_get_has_selection (buffer))
2867 return NULL((void*)0);
2868 else
2869 {
2870 CtkTextIter start, end;
2871 gchar *retval;
2872
2873 ctk_text_buffer_get_selection_bounds (buffer, &start, &end);
2874 retval = ctk_text_buffer_get_text (buffer, &start, &end, FALSE(0));
2875
2876 return retval;
2877 }
2878}