1 /* gdict-speller.c - display widget for dictionary matches
2  *
3  * Copyright (C) 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 Lesser General Public License
16  * along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * SECTION:gdict-speller
21  * @short_description: Display matching words
22  *
23  * #GdictSpeller is a widget showing a list of words returned by a
24  * #GdictContext query, using a specific database and a matching strategy.
25  */
26 
27 #include "config.h"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33 
34 #include <gdk/gdkkeysyms.h>
35 #include <gtk/gtk.h>
36 #include <glib/gi18n-lib.h>
37 
38 #include "gdict-speller.h"
39 #include "gdict-utils.h"
40 #include "gdict-enum-types.h"
41 #include "gdict-marshal.h"
42 #include "gdict-debug.h"
43 #include "gdict-private.h"
44 
45 struct _GdictSpellerPrivate
46 {
47   GdictContext *context;
48   gchar *database;
49   gchar *strategy;
50 
51   gchar *word;
52 
53   GtkWidget *treeview;
54   GtkWidget *clear_button;
55 
56   GdkCursor *busy_cursor;
57 
58   GtkListStore *store;
59   gint results;
60 
61   guint start_id;
62   guint end_id;
63   guint match_id;
64   guint error_id;
65 
66   guint is_searching : 1;
67 };
68 
69 typedef enum
70 {
71   MATCH_DB,
72   MATCH_WORD,
73   MATCH_ERROR
74 } MatchType;
75 
76 enum
77 {
78   MATCH_COLUMN_TYPE,
79   MATCH_COLUMN_DB_NAME,
80   MATCH_COLUMN_WORD,
81 
82   MATCH_N_COLUMNS
83 };
84 
85 enum
86 {
87   PROP_0,
88 
89   PROP_CONTEXT,
90   PROP_WORD,
91   PROP_DATABASE,
92   PROP_STRATEGY,
93   PROP_COUNT
94 };
95 
96 enum
97 {
98   WORD_ACTIVATED,
99 
100   LAST_SIGNAL
101 };
102 
103 static guint speller_signals[LAST_SIGNAL] = { 0 };
104 
G_DEFINE_TYPE_WITH_PRIVATE(GdictSpeller,gdict_speller,GTK_TYPE_BOX)105 G_DEFINE_TYPE_WITH_PRIVATE (GdictSpeller, gdict_speller, GTK_TYPE_BOX)
106 
107 static void
108 set_gdict_context (GdictSpeller *speller,
109 		   GdictContext *context)
110 {
111   GdictSpellerPrivate *priv;
112 
113   g_assert (GDICT_IS_SPELLER (speller));
114 
115   priv = speller->priv;
116   if (priv->context)
117     {
118       if (priv->start_id)
119         {
120           GDICT_NOTE (SPELLER, "Removing old context handlers");
121 
122           g_signal_handler_disconnect (priv->context, priv->start_id);
123           g_signal_handler_disconnect (priv->context, priv->match_id);
124           g_signal_handler_disconnect (priv->context, priv->end_id);
125 
126           priv->start_id = 0;
127           priv->end_id = 0;
128           priv->match_id = 0;
129         }
130 
131       if (priv->error_id)
132         {
133           g_signal_handler_disconnect (priv->context, priv->error_id);
134 
135           priv->error_id = 0;
136         }
137 
138       GDICT_NOTE (SPELLER, "Removing old context");
139 
140       g_object_unref (G_OBJECT (priv->context));
141     }
142 
143   if (!context)
144     return;
145 
146   if (!GDICT_IS_CONTEXT (context))
147     {
148       g_warning ("Object of type `%s' instead of a GdictContext\n",
149       		 g_type_name (G_OBJECT_TYPE (context)));
150       return;
151     }
152 
153   GDICT_NOTE (SPELLER, "Setting new context\n");
154 
155   priv->context = context;
156   g_object_ref (G_OBJECT (priv->context));
157 }
158 
159 static void
gdict_speller_finalize(GObject * gobject)160 gdict_speller_finalize (GObject *gobject)
161 {
162   GdictSpeller *speller = GDICT_SPELLER (gobject);
163   GdictSpellerPrivate *priv = speller->priv;
164 
165   if (priv->context)
166     set_gdict_context (speller, NULL);
167 
168   g_clear_object (&priv->busy_cursor);
169 
170   g_free (priv->strategy);
171   g_free (priv->database);
172   g_free (priv->word);
173 
174   if (priv->store)
175     g_object_unref (priv->store);
176 
177   G_OBJECT_CLASS (gdict_speller_parent_class)->finalize (gobject);
178 }
179 
180 
181 static void
gdict_speller_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)182 gdict_speller_set_property (GObject      *gobject,
183 			    guint         prop_id,
184 			    const GValue *value,
185 			    GParamSpec   *pspec)
186 {
187   GdictSpeller *speller = GDICT_SPELLER (gobject);
188   GdictSpellerPrivate *priv = speller->priv;
189 
190   switch (prop_id)
191     {
192     case PROP_CONTEXT:
193       set_gdict_context (speller, g_value_get_object (value));
194       break;
195     case PROP_DATABASE:
196       g_free (priv->database);
197       priv->database = g_strdup (g_value_get_string (value));
198       break;
199     case PROP_STRATEGY:
200       g_free (priv->strategy);
201       priv->strategy = g_strdup (g_value_get_string (value));
202       break;
203     default:
204       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
205       break;
206     }
207 }
208 
209 static void
gdict_speller_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)210 gdict_speller_get_property (GObject    *gobject,
211 			    guint       prop_id,
212 			    GValue     *value,
213 			    GParamSpec *pspec)
214 {
215   GdictSpeller *speller = GDICT_SPELLER (gobject);
216 
217   switch (prop_id)
218     {
219     case PROP_DATABASE:
220       g_value_set_string (value, speller->priv->database);
221       break;
222     case PROP_STRATEGY:
223       g_value_set_string (value, speller->priv->strategy);
224       break;
225     case PROP_CONTEXT:
226       g_value_set_object (value, speller->priv->context);
227       break;
228     case PROP_COUNT:
229       g_value_set_int (value, speller->priv->results);
230       break;
231     default:
232       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
233       break;
234     }
235 }
236 
237 static void
row_activated_cb(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)238 row_activated_cb (GtkTreeView       *treeview,
239 		  GtkTreePath       *path,
240 		  GtkTreeViewColumn *column,
241 		  gpointer           user_data)
242 {
243   GdictSpeller *speller = GDICT_SPELLER (user_data);
244   GdictSpellerPrivate *priv = speller->priv;
245   GtkTreeIter iter;
246   gchar *word, *db_name;
247   gboolean valid;
248 
249   valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store),
250 			           &iter,
251 				   path);
252   if (!valid)
253     {
254       g_warning ("Invalid iterator found");
255 
256       return;
257     }
258 
259   gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
260 		      MATCH_COLUMN_WORD, &word,
261 		      MATCH_COLUMN_DB_NAME, &db_name,
262 		      -1);
263   if (word)
264     g_signal_emit (speller, speller_signals[WORD_ACTIVATED], 0,
265 		   word, db_name);
266   else
267     {
268       gchar *row = gtk_tree_path_to_string (path);
269 
270       g_warning ("Row %s activated, but no word attached", row);
271       g_free (row);
272     }
273 
274   g_free (word);
275   g_free (db_name);
276 }
277 
278 static void
clear_button_clicked_cb(GtkWidget * widget,gpointer user_data)279 clear_button_clicked_cb (GtkWidget *widget,
280 			 gpointer   user_data)
281 {
282   GdictSpeller *speller = GDICT_SPELLER (user_data);
283 
284   gdict_speller_clear (speller);
285 }
286 
287 static GObject *
gdict_speller_constructor(GType type,guint n_params,GObjectConstructParam * params)288 gdict_speller_constructor (GType                  type,
289 			   guint                  n_params,
290 			   GObjectConstructParam *params)
291 {
292   GObject *object;
293   GdictSpeller *speller;
294   GdictSpellerPrivate *priv;
295   GtkWidget *sw;
296   GtkCellRenderer *renderer;
297   GtkTreeViewColumn *column;
298   GtkWidget *hbox;
299 
300   object = G_OBJECT_CLASS (gdict_speller_parent_class)->constructor (type,
301   						                     n_params,
302 								     params);
303   speller = GDICT_SPELLER (object);
304   priv = speller->priv;
305 
306   sw = gtk_scrolled_window_new (NULL, NULL);
307   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
308   				  GTK_POLICY_AUTOMATIC,
309   				  GTK_POLICY_AUTOMATIC);
310   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
311   				       GTK_SHADOW_IN);
312   gtk_box_pack_start (GTK_BOX (speller), sw, TRUE, TRUE, 0);
313   gtk_widget_show (sw);
314 
315   renderer = gtk_cell_renderer_text_new ();
316   column = gtk_tree_view_column_new_with_attributes ("matches",
317 		  				     renderer,
318 						     "text", MATCH_COLUMN_WORD,
319 						     NULL);
320 
321   priv->treeview = gtk_tree_view_new ();
322   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
323 		           GTK_TREE_MODEL (priv->store));
324   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->treeview), FALSE);
325   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->treeview), column);
326   g_signal_connect (priv->treeview, "row-activated",
327 		    G_CALLBACK (row_activated_cb), speller);
328   gtk_container_add (GTK_CONTAINER (sw), priv->treeview);
329   gtk_widget_show (priv->treeview);
330 
331   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
332 
333   priv->clear_button = gtk_button_new_from_icon_name ("edit-clear-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
334   gtk_button_set_always_show_image (GTK_BUTTON (priv->clear_button), TRUE);
335   g_signal_connect (priv->clear_button, "clicked",
336 		    G_CALLBACK (clear_button_clicked_cb),
337 		    speller);
338   gtk_box_pack_start (GTK_BOX (hbox), priv->clear_button, FALSE, FALSE, 0);
339   gtk_widget_show (priv->clear_button);
340   gtk_widget_set_tooltip_text (priv->clear_button, _("Clear the list of similar words"));
341 
342   gtk_box_pack_end (GTK_BOX (speller), hbox, FALSE, FALSE, 0);
343   gtk_widget_show (hbox);
344 
345   return object;
346 }
347 
348 static void
gdict_speller_class_init(GdictSpellerClass * klass)349 gdict_speller_class_init (GdictSpellerClass *klass)
350 {
351   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
352 
353   gobject_class->finalize = gdict_speller_finalize;
354   gobject_class->set_property = gdict_speller_set_property;
355   gobject_class->get_property = gdict_speller_get_property;
356   gobject_class->constructor = gdict_speller_constructor;
357 
358   g_object_class_install_property (gobject_class,
359   				   PROP_CONTEXT,
360   				   g_param_spec_object ("context",
361                                                         "Context",
362                                                         "The GdictContext object used to get the word definition",
363   				   			GDICT_TYPE_CONTEXT,
364   				   			(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)));
365   g_object_class_install_property (gobject_class,
366 		  		   PROP_DATABASE,
367 				   g_param_spec_string ("database",
368                                                         "Database",
369                                                         "The database used to query the GdictContext",
370 							GDICT_DEFAULT_DATABASE,
371 							(G_PARAM_READABLE | G_PARAM_WRITABLE)));
372   g_object_class_install_property (gobject_class,
373 		  		   PROP_DATABASE,
374 				   g_param_spec_string ("strategy",
375                                                         "Strategy",
376                                                         "The strategy used to query the GdictContext",
377 							GDICT_DEFAULT_STRATEGY,
378 							(G_PARAM_READABLE | G_PARAM_WRITABLE)));
379 
380   speller_signals[WORD_ACTIVATED] =
381     g_signal_new ("word-activated",
382 		  G_OBJECT_CLASS_TYPE (gobject_class),
383 		  G_SIGNAL_RUN_LAST,
384 		  G_STRUCT_OFFSET (GdictSpellerClass, word_activated),
385 		  NULL, NULL,
386 		  gdict_marshal_VOID__STRING_STRING,
387 		  G_TYPE_NONE, 2,
388 		  G_TYPE_STRING,
389 		  G_TYPE_STRING);
390 }
391 
392 static void
gdict_speller_init(GdictSpeller * speller)393 gdict_speller_init (GdictSpeller *speller)
394 {
395   GdictSpellerPrivate *priv;
396 
397   speller->priv = priv = gdict_speller_get_instance_private (speller);
398 
399   gtk_orientable_set_orientation (GTK_ORIENTABLE (speller), GTK_ORIENTATION_VERTICAL);
400 
401   priv->database = NULL;
402   priv->strategy = NULL;
403   priv->word = NULL;
404 
405   priv->results = -1;
406   priv->context = NULL;
407 
408   priv->store = gtk_list_store_new (MATCH_N_COLUMNS,
409 		                    G_TYPE_INT,    /* MatchType */
410 		                    G_TYPE_STRING, /* db_name */
411 				    G_TYPE_STRING  /* word */);
412 
413   priv->start_id = 0;
414   priv->end_id = 0;
415   priv->match_id = 0;
416   priv->error_id = 0;
417 }
418 
419 /**
420  * gdict_speller_new:
421  *
422  * FIXME
423  *
424  * Return value: FIXME
425  *
426  * Since:
427  */
428 GtkWidget *
gdict_speller_new(void)429 gdict_speller_new (void)
430 {
431   return g_object_new (GDICT_TYPE_SPELLER, NULL);
432 }
433 
434 /**
435  * gdict_speller_new_with_context:
436  * @context: a #GdictContext
437  *
438  * FIXME
439  *
440  * Return value: FIXME
441  *
442  * Since:
443  */
444 GtkWidget *
gdict_speller_new_with_context(GdictContext * context)445 gdict_speller_new_with_context (GdictContext *context)
446 {
447   g_return_val_if_fail (GDICT_IS_CONTEXT (context), NULL);
448 
449   return g_object_new (GDICT_TYPE_SPELLER,
450 		       "context", context,
451 		       NULL);
452 }
453 
454 /**
455  * gdict_speller_set_context:
456  * @speller: a #GdictSpeller
457  * @context: a #GdictContext
458  *
459  * FIXME
460  *
461  * Since:
462  */
463 void
gdict_speller_set_context(GdictSpeller * speller,GdictContext * context)464 gdict_speller_set_context (GdictSpeller *speller,
465 			   GdictContext *context)
466 {
467   g_return_if_fail (GDICT_IS_SPELLER (speller));
468   g_return_if_fail (context == NULL || GDICT_IS_CONTEXT (context));
469 
470   set_gdict_context (speller, context);
471 
472   g_object_notify (G_OBJECT (speller), "context");
473 }
474 
475 /**
476  * gdict_speller_get_context:
477  * @speller: a #GdictSpeller
478  *
479  * FIXME
480  *
481  * Return value: (transfer none): a #GdictContext
482  *
483  * Since:
484  */
485 GdictContext *
gdict_speller_get_context(GdictSpeller * speller)486 gdict_speller_get_context (GdictSpeller *speller)
487 {
488   g_return_val_if_fail (GDICT_IS_SPELLER (speller), NULL);
489 
490   return speller->priv->context;
491 }
492 
493 /**
494  * gdict_speller_set_database:
495  * @speller: a #GdictSpeller
496  * @database: FIXME
497  *
498  * FIXME
499  *
500  * Since:
501  */
502 void
gdict_speller_set_database(GdictSpeller * speller,const gchar * database)503 gdict_speller_set_database (GdictSpeller *speller,
504 			    const gchar  *database)
505 {
506   GdictSpellerPrivate *priv;
507 
508   g_return_if_fail (GDICT_IS_SPELLER (speller));
509 
510   priv = speller->priv;
511 
512   if (!database || database[0] == '\0')
513     database = GDICT_DEFAULT_DATABASE;
514 
515   g_free (priv->database);
516   priv->database = g_strdup (database);
517 
518   g_object_notify (G_OBJECT (speller), "database");
519 }
520 
521 /**
522  * gdict_speller_get_database:
523  * @speller: a #GdictSpeller
524  *
525  * FIXME
526  *
527  * Return value: FIXME
528  *
529  * Since: FIXME
530  */
531 const gchar *
gdict_speller_get_database(GdictSpeller * speller)532 gdict_speller_get_database (GdictSpeller *speller)
533 {
534   g_return_val_if_fail (GDICT_IS_SPELLER (speller), NULL);
535 
536   return speller->priv->database;
537 }
538 
539 /**
540  * gdict_speller_set_strategy:
541  * @speller: a #GdictSpeller
542  * @strategy: FIXME
543  *
544  * FIXME
545  *
546  * Since: FIXME
547  */
548 void
gdict_speller_set_strategy(GdictSpeller * speller,const gchar * strategy)549 gdict_speller_set_strategy (GdictSpeller *speller,
550 			    const gchar  *strategy)
551 {
552   GdictSpellerPrivate *priv;
553 
554   g_return_if_fail (GDICT_IS_SPELLER (speller));
555 
556   priv = speller->priv;
557 
558   if (!strategy || strategy[0] == '\0')
559     strategy = GDICT_DEFAULT_STRATEGY;
560 
561   g_free (priv->strategy);
562   priv->strategy = g_strdup (strategy);
563 
564   g_object_notify (G_OBJECT (speller), "strategy");
565 }
566 
567 /**
568  * gdict_speller_get_strategy:
569  * @speller: a #GdictSpeller
570  *
571  * FIXME
572  *
573  * Return value: FIXME
574  *
575  * Since: FIXME
576  */
577 const gchar *
gdict_speller_get_strategy(GdictSpeller * speller)578 gdict_speller_get_strategy (GdictSpeller *speller)
579 {
580   g_return_val_if_fail (GDICT_IS_SPELLER (speller), NULL);
581 
582   return speller->priv->strategy;
583 }
584 
585 /**
586  * gdict_speller_clear:
587  * @speller: a #GdictSpeller
588  *
589  * FIXME
590  *
591  * Since: FIXME
592  */
593 void
gdict_speller_clear(GdictSpeller * speller)594 gdict_speller_clear (GdictSpeller *speller)
595 {
596   GdictSpellerPrivate *priv;
597 
598   g_return_if_fail (GDICT_IS_SPELLER (speller));
599   priv = speller->priv;
600 
601   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview), NULL);
602 
603   gtk_list_store_clear (priv->store);
604   priv->results = -1;
605 
606   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
607 		           GTK_TREE_MODEL (priv->store));
608 }
609 
610 static void
lookup_start_cb(GdictContext * context,gpointer user_data)611 lookup_start_cb (GdictContext *context,
612 		 gpointer      user_data)
613 {
614   GdictSpeller *speller = GDICT_SPELLER (user_data);
615   GdictSpellerPrivate *priv = speller->priv;
616 
617   if (!priv->busy_cursor)
618     {
619       GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (speller));
620       priv->busy_cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
621     }
622 
623   if (gtk_widget_get_window (GTK_WIDGET (speller)))
624     gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (speller)), priv->busy_cursor);
625 
626   priv->is_searching = TRUE;
627 }
628 
629 static void
lookup_end_cb(GdictContext * context,gpointer user_data)630 lookup_end_cb (GdictContext *context,
631 	       gpointer      user_data)
632 {
633   GdictSpeller *speller = GDICT_SPELLER (user_data);
634   GdictSpellerPrivate *priv = speller->priv;
635 
636   if (gtk_widget_get_window (GTK_WIDGET (speller)))
637     gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (speller)), NULL);
638 
639   g_free (priv->word);
640   priv->word = NULL;
641 
642   priv->is_searching = FALSE;
643 }
644 
645 static void
match_found_cb(GdictContext * context,GdictMatch * match,gpointer user_data)646 match_found_cb (GdictContext *context,
647 		GdictMatch   *match,
648 		gpointer      user_data)
649 {
650   GdictSpeller *speller = GDICT_SPELLER (user_data);
651   GdictSpellerPrivate *priv = speller->priv;
652   GtkTreeIter iter;
653 
654   GDICT_NOTE (SPELLER, "MATCH: `%s' (from `%s')",
655               gdict_match_get_word (match),
656               gdict_match_get_database (match));
657 
658   gtk_list_store_append (priv->store, &iter);
659   gtk_list_store_set (priv->store, &iter,
660 		      MATCH_COLUMN_TYPE, MATCH_WORD,
661 		      MATCH_COLUMN_DB_NAME, gdict_match_get_database (match),
662 		      MATCH_COLUMN_WORD, gdict_match_get_word (match),
663 		      -1);
664 
665   if (priv->results == -1)
666     priv->results = 1;
667   else
668     priv->results += 1;
669 }
670 
671 static void
error_cb(GdictContext * context,const GError * error,gpointer user_data)672 error_cb (GdictContext *context,
673 	  const GError *error,
674 	  gpointer      user_data)
675 {
676   GdictSpeller *speller = GDICT_SPELLER (user_data);
677   GdictSpellerPrivate *priv = speller->priv;
678 
679   gdict_speller_clear (speller);
680 
681   if (gtk_widget_get_window (GTK_WIDGET (speller)))
682     gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (speller)), NULL);
683 
684   g_free (priv->word);
685   priv->word = NULL;
686 
687   priv->is_searching = FALSE;
688 }
689 
690 /**
691  * gdict_speller_match:
692  * @speller: a #GdictSpeller
693  * @word: FIXME
694  *
695  * FIXME
696  *
697  * Since: FIXME
698  */
699 void
gdict_speller_match(GdictSpeller * speller,const gchar * word)700 gdict_speller_match (GdictSpeller *speller,
701 		     const gchar  *word)
702 {
703   GdictSpellerPrivate *priv;
704   GError *match_error;
705 
706   g_return_if_fail (GDICT_IS_SPELLER (speller));
707   g_return_if_fail (word != NULL);
708 
709   priv = speller->priv;
710 
711   if (!priv->context)
712     {
713       g_warning ("Attempting to match `%s', but no GdictContext "
714 		 "has been set.  Use gdict_speller_set_context() "
715 		 "before invoking gdict_speller_match().",
716 		 word);
717 
718       return;
719     }
720 
721   if (priv->is_searching)
722     {
723       _gdict_show_error_dialog (NULL,
724                                 _("Another search is in progress"),
725                                 _("Please wait until the current search ends."));
726 
727       return;
728     }
729 
730   gdict_speller_clear (speller);
731 
732   if (!priv->start_id)
733     {
734       priv->start_id = g_signal_connect (priv->context, "lookup-start",
735 		                         G_CALLBACK (lookup_start_cb),
736 					 speller);
737       priv->match_id = g_signal_connect (priv->context, "match-found",
738                                          G_CALLBACK (match_found_cb),
739 					 speller);
740       priv->end_id = g_signal_connect (priv->context, "lookup-end",
741 		      		       G_CALLBACK (lookup_end_cb),
742 				       speller);
743     }
744 
745   if (!priv->error_id)
746     priv->error_id = g_signal_connect (priv->context, "error",
747 		    		       G_CALLBACK (error_cb),
748 				       speller);
749 
750   g_free (priv->word);
751   priv->word = g_strdup (word);
752 
753   match_error = NULL;
754   gdict_context_match_word (priv->context,
755 		  	    priv->database,
756 			    priv->strategy,
757 			    priv->word,
758 			    &match_error);
759   if (match_error)
760     {
761       GtkTreeIter iter;
762 
763       gtk_list_store_append (priv->store, &iter);
764       gtk_list_store_set (priv->store, &iter,
765 		          MATCH_COLUMN_TYPE, MATCH_ERROR,
766 			  MATCH_COLUMN_DB_NAME, _("Error while matching"),
767 			  MATCH_COLUMN_WORD, NULL,
768 			  -1);
769 
770       g_warning ("Error while matching `%s': %s",
771                  priv->word,
772                  match_error->message);
773 
774       g_error_free (match_error);
775     }
776 }
777 
778 /**
779  * gdict_speller_count_match:
780  * @speller: a #GdictSpeller
781  *
782  * FIXME
783  *
784  * Return value: FIXME
785  *
786  * Since: FIXME
787  */
788 gint
gdict_speller_count_matches(GdictSpeller * speller)789 gdict_speller_count_matches (GdictSpeller *speller)
790 {
791   g_return_val_if_fail (GDICT_IS_SPELLER (speller), -1);
792 
793   return speller->priv->results;
794 }
795 
796 /**
797  * gdict_speller_get_matches:
798  * @speller: a #GdictSpeller
799  * @length: FIXME
800  *
801  * FIXME
802  *
803  * Return value: (transfer full): FIXME
804  *
805  * Since: FIXME
806  */
807 gchar **
gdict_speller_get_matches(GdictSpeller * speller,gsize length)808 gdict_speller_get_matches (GdictSpeller *speller,
809 			   gsize         length)
810 {
811   g_return_val_if_fail (GDICT_IS_SPELLER (speller), NULL);
812 
813   return NULL;
814 }
815