1 /* gdict-strategy-chooser.c - display widget for strategy names
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-strategy-chooser
21  * @short_description: Display a list of matching strategies
22  *
23  * Each #GdictContext allows matching a word using a specific "matching
24  * strategy". The #GdictStrategyChooser widget queries a #GdictContext and
25  * displays the list of available matching strategies.
26  */
27 
28 #include "config.h"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdarg.h>
34 
35 #include <gtk/gtk.h>
36 #include <glib/gi18n-lib.h>
37 
38 #include "gdict-strategy-chooser.h"
39 #include "gdict-utils.h"
40 #include "gdict-debug.h"
41 #include "gdict-private.h"
42 #include "gdict-enum-types.h"
43 #include "gdict-marshal.h"
44 
45 struct _GdictStrategyChooserPrivate
46 {
47   GtkListStore *store;
48 
49   GtkWidget *treeview;
50   GtkWidget *clear_button;
51   GtkWidget *refresh_button;
52   GtkWidget *buttons_box;
53 
54   GdictContext *context;
55   gint results;
56 
57   guint start_id;
58   guint match_id;
59   guint end_id;
60   guint error_id;
61 
62   GdkCursor *busy_cursor;
63 
64   gchar *current_strat;
65 
66   guint is_searching : 1;
67 };
68 
69 enum
70 {
71   STRATEGY_NAME,
72   STRATEGY_ERROR
73 } StratType;
74 
75 enum
76 {
77   STRAT_COLUMN_TYPE,
78   STRAT_COLUMN_NAME,
79   STRAT_COLUMN_DESCRIPTION,
80   STRAT_COLUMN_CURRENT,
81 
82   STRAT_N_COLUMNS
83 };
84 
85 enum
86 {
87   PROP_0,
88 
89   PROP_CONTEXT,
90   PROP_COUNT
91 };
92 
93 enum
94 {
95   STRATEGY_ACTIVATED,
96   CLOSED,
97 
98   LAST_SIGNAL
99 };
100 
101 static guint db_chooser_signals[LAST_SIGNAL] = { 0 };
102 
G_DEFINE_TYPE_WITH_PRIVATE(GdictStrategyChooser,gdict_strategy_chooser,GTK_TYPE_BOX)103 G_DEFINE_TYPE_WITH_PRIVATE (GdictStrategyChooser, gdict_strategy_chooser, GTK_TYPE_BOX)
104 
105 static void
106 set_gdict_context (GdictStrategyChooser *chooser,
107 		   GdictContext         *context)
108 {
109   GdictStrategyChooserPrivate *priv;
110 
111   g_assert (GDICT_IS_STRATEGY_CHOOSER (chooser));
112   priv = chooser->priv;
113 
114   if (priv->context)
115     {
116       if (priv->start_id)
117         {
118           GDICT_NOTE (CHOOSER, "Removing old context handlers");
119 
120           g_signal_handler_disconnect (priv->context, priv->start_id);
121           g_signal_handler_disconnect (priv->context, priv->match_id);
122           g_signal_handler_disconnect (priv->context, priv->end_id);
123 
124           priv->start_id = 0;
125           priv->end_id = 0;
126           priv->match_id = 0;
127         }
128 
129       if (priv->error_id)
130         {
131           g_signal_handler_disconnect (priv->context, priv->error_id);
132 
133           priv->error_id = 0;
134         }
135 
136       GDICT_NOTE (CHOOSER, "Removing old context");
137 
138       g_object_unref (G_OBJECT (priv->context));
139 
140       priv->context = NULL;
141       priv->results = -1;
142     }
143 
144   if (!context)
145     return;
146 
147   if (!GDICT_IS_CONTEXT (context))
148     {
149       g_warning ("Object of type '%s' instead of a GdictContext\n",
150       		 g_type_name (G_OBJECT_TYPE (context)));
151       return;
152     }
153 
154   GDICT_NOTE (CHOOSER, "Setting new context");
155 
156   priv->context = g_object_ref (context);
157   priv->results = 0;
158 }
159 
160 static void
gdict_strategy_chooser_dispose(GObject * gobject)161 gdict_strategy_chooser_dispose (GObject *gobject)
162 {
163   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (gobject);
164   GdictStrategyChooserPrivate *priv = chooser->priv;
165 
166   set_gdict_context (chooser, NULL);
167 
168   g_clear_object (&priv->busy_cursor);
169 
170   if (priv->store)
171     {
172       g_object_unref (priv->store);
173       priv->store = NULL;
174     }
175 
176   G_OBJECT_CLASS (gdict_strategy_chooser_parent_class)->dispose (gobject);
177 }
178 
179 static void
gdict_strategy_chooser_finalize(GObject * gobject)180 gdict_strategy_chooser_finalize (GObject *gobject)
181 {
182   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (gobject);
183   GdictStrategyChooserPrivate *priv = chooser->priv;
184 
185   g_free (priv->current_strat);
186 
187   G_OBJECT_CLASS (gdict_strategy_chooser_parent_class)->finalize (gobject);
188 }
189 
190 static void
gdict_strategy_chooser_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)191 gdict_strategy_chooser_set_property (GObject      *gobject,
192 				     guint         prop_id,
193 				     const GValue *value,
194 				     GParamSpec   *pspec)
195 {
196   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (gobject);
197 
198   switch (prop_id)
199     {
200     case PROP_CONTEXT:
201       set_gdict_context (chooser, g_value_get_object (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_strategy_chooser_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)210 gdict_strategy_chooser_get_property (GObject    *gobject,
211 				     guint       prop_id,
212 				     GValue     *value,
213 				     GParamSpec *pspec)
214 {
215   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (gobject);
216 
217   switch (prop_id)
218     {
219     case PROP_CONTEXT:
220       g_value_set_object (value, chooser->priv->context);
221       break;
222     case PROP_COUNT:
223       g_value_set_int (value, chooser->priv->results);
224       break;
225     default:
226       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
227       break;
228     }
229 }
230 
231 static void
row_activated_cb(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)232 row_activated_cb (GtkTreeView       *treeview,
233 		  GtkTreePath       *path,
234 		  GtkTreeViewColumn *column,
235 		  gpointer           user_data)
236 {
237   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (user_data);
238   GdictStrategyChooserPrivate *priv = chooser->priv;
239   GtkTreeIter iter;
240   gchar *db_name, *db_desc;
241   gboolean valid;
242 
243   valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store),
244 		  		   &iter,
245 				   path);
246   if (!valid)
247     {
248       g_warning ("Invalid iterator found");
249       return;
250     }
251 
252   gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
253 		      STRAT_COLUMN_NAME, &db_name,
254 		      STRAT_COLUMN_DESCRIPTION, &db_desc,
255 		      -1);
256   if (db_name && db_desc)
257     {
258       g_signal_emit (chooser, db_chooser_signals[STRATEGY_ACTIVATED], 0,
259 		     db_name, db_desc);
260     }
261   else
262     {
263       gchar *row = gtk_tree_path_to_string (path);
264 
265       g_warning ("Row %s activated, but no strategy attached", row);
266       g_free (row);
267     }
268 
269   g_free (db_name);
270   g_free (db_desc);
271 }
272 
273 static void
refresh_button_clicked_cb(GtkWidget * widget,gpointer user_data)274 refresh_button_clicked_cb (GtkWidget *widget,
275 			   gpointer   user_data)
276 {
277   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (user_data);
278 
279   gdict_strategy_chooser_refresh (chooser);
280 }
281 
282 static void
clear_button_clicked_cb(GtkWidget * widget,gpointer user_data)283 clear_button_clicked_cb (GtkWidget *widget,
284 			 gpointer   user_data)
285 {
286   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (user_data);
287 
288   gdict_strategy_chooser_clear (chooser);
289 }
290 
291 static GObject *
gdict_strategy_chooser_constructor(GType type,guint n_params,GObjectConstructParam * params)292 gdict_strategy_chooser_constructor (GType                  type,
293 				    guint                  n_params,
294 				    GObjectConstructParam *params)
295 {
296   GObject *object;
297   GdictStrategyChooser *chooser;
298   GdictStrategyChooserPrivate *priv;
299   GtkWidget *sw;
300   GtkCellRenderer *renderer;
301   GtkTreeViewColumn *column;
302   GtkWidget *hbox;
303 
304   object = G_OBJECT_CLASS (gdict_strategy_chooser_parent_class)->constructor (type,
305 		  							      n_params,
306 									      params);
307 
308   chooser = GDICT_STRATEGY_CHOOSER (object);
309   priv = chooser->priv;
310 
311   sw = gtk_scrolled_window_new (NULL, NULL);
312   gtk_widget_set_hexpand(sw, TRUE);
313   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
314 		  		  GTK_POLICY_AUTOMATIC,
315 				  GTK_POLICY_AUTOMATIC);
316   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
317 		  		       GTK_SHADOW_IN);
318   gtk_box_pack_start (GTK_BOX (chooser), sw, TRUE, TRUE, 0);
319   gtk_widget_show (sw);
320 
321   renderer = gtk_cell_renderer_text_new ();
322   column = gtk_tree_view_column_new_with_attributes ("strategies",
323 		  				     renderer,
324 						     "text", STRAT_COLUMN_DESCRIPTION,
325                                                      "weight", STRAT_COLUMN_CURRENT,
326 						     NULL);
327   priv->treeview = gtk_tree_view_new ();
328   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
329 		  	   GTK_TREE_MODEL (priv->store));
330   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->treeview), FALSE);
331   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->treeview), column);
332   g_signal_connect (priv->treeview, "row-activated",
333 		    G_CALLBACK (row_activated_cb), chooser);
334   gtk_container_add (GTK_CONTAINER (sw), priv->treeview);
335   gtk_widget_show (priv->treeview);
336 
337   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
338 
339   priv->refresh_button = gtk_button_new ();
340   gtk_button_set_image (GTK_BUTTON (priv->refresh_button),
341 		        gtk_image_new_from_icon_name ("view-refresh-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR));
342   g_signal_connect (priv->refresh_button, "clicked",
343 		    G_CALLBACK (refresh_button_clicked_cb),
344 		    chooser);
345   gtk_box_pack_start (GTK_BOX (hbox), priv->refresh_button, FALSE, FALSE, 0);
346   gtk_widget_show (priv->refresh_button);
347   gtk_widget_set_tooltip_text (priv->refresh_button,
348                                _("Reload the list of available strategies"));
349 
350   priv->clear_button = gtk_button_new ();
351   gtk_button_set_image (GTK_BUTTON (priv->clear_button),
352 		        gtk_image_new_from_icon_name ("edit-clear-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR));
353   g_signal_connect (priv->clear_button, "clicked",
354 		    G_CALLBACK (clear_button_clicked_cb),
355 		    chooser);
356   gtk_box_pack_start (GTK_BOX (hbox), priv->clear_button, FALSE, FALSE, 0);
357   gtk_widget_show (priv->clear_button);
358   gtk_widget_set_tooltip_text (priv->clear_button,
359                                _("Clear the list of available strategies"));
360 
361   gtk_box_pack_end (GTK_BOX (chooser), hbox, FALSE, FALSE, 0);
362   gtk_widget_show (hbox);
363 
364   return object;
365 }
366 
367 static void
gdict_strategy_chooser_class_init(GdictStrategyChooserClass * klass)368 gdict_strategy_chooser_class_init (GdictStrategyChooserClass *klass)
369 {
370   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
371 
372   gobject_class->finalize = gdict_strategy_chooser_finalize;
373   gobject_class->dispose = gdict_strategy_chooser_dispose;
374   gobject_class->set_property = gdict_strategy_chooser_set_property;
375   gobject_class->get_property = gdict_strategy_chooser_get_property;
376   gobject_class->constructor = gdict_strategy_chooser_constructor;
377 
378   /**
379    * GdictStrategyChooser:context:
380    *
381    * The #GdictContext object used to retrieve the list of strategies.
382    */
383   g_object_class_install_property (gobject_class,
384   				   PROP_CONTEXT,
385   				   g_param_spec_object ("context",
386   				   			"Context",
387   				   			"The GdictContext object used to get the list of strategies",
388   				   			GDICT_TYPE_CONTEXT,
389   				   			(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)));
390 
391   /**
392    * GdictStrategyChooser::strategy-activated:
393    * @chooser: the widget that received the signal
394    * @name: the name of the activated strategy
395    * @description: the description of the activate strategy
396    *
397    * The ::strategy-activated signal is emitted each time the user
398    * activates a strategy in the @chooser, either by double click or
399    * using the keyboard.
400    */
401   db_chooser_signals[STRATEGY_ACTIVATED] =
402     g_signal_new ("strategy-activated",
403 		  G_OBJECT_CLASS_TYPE (gobject_class),
404 		  G_SIGNAL_RUN_LAST,
405 		  G_STRUCT_OFFSET (GdictStrategyChooserClass, strategy_activated),
406 		  NULL, NULL,
407 		  gdict_marshal_VOID__STRING_STRING,
408 		  G_TYPE_NONE, 2,
409 		  G_TYPE_STRING,
410 		  G_TYPE_STRING);
411 }
412 
413 static void
gdict_strategy_chooser_init(GdictStrategyChooser * chooser)414 gdict_strategy_chooser_init (GdictStrategyChooser *chooser)
415 {
416   GdictStrategyChooserPrivate *priv;
417 
418   chooser->priv = priv = gdict_strategy_chooser_get_instance_private (chooser);
419 
420   gtk_orientable_set_orientation (GTK_ORIENTABLE (chooser), GTK_ORIENTATION_VERTICAL);
421 
422   priv->results = -1;
423   priv->context = NULL;
424 
425   priv->store = gtk_list_store_new (STRAT_N_COLUMNS,
426 		                    G_TYPE_INT,    /* strat_type */
427 		                    G_TYPE_STRING, /* strat_name */
428 				    G_TYPE_STRING, /* strat_desc */
429                                     G_TYPE_INT     /* strat_current */);
430 
431   priv->start_id = 0;
432   priv->end_id = 0;
433   priv->match_id = 0;
434   priv->error_id = 0;
435 }
436 
437 /**
438  * gdict_strategy_chooser_new:
439  *
440  * Creates a new #GdictStrategyChooser. Use this widget to show a list
441  * of matching strategies available on a dictionary source represented
442  * by a #GdictContext, set with gdict_strategy_chooser_set_context().
443  *
444  * Return value: the newly created #GdictStrategyChooser widget
445  *
446  * Since: 0.9
447  */
448 GtkWidget *
gdict_strategy_chooser_new(void)449 gdict_strategy_chooser_new (void)
450 {
451   return g_object_new (GDICT_TYPE_STRATEGY_CHOOSER, NULL);
452 }
453 
454 /**
455  * gdict_strategy_chooser_new_with_context:
456  * @context: a #GdictContext
457  *
458  * Creates a new #GdictStrategyChooser widget, using @context as the
459  * representation of a dictionary source.
460  *
461  * Return value: the newly created #GdictStrategyChooser widget
462  *
463  * Since: 0.9
464  */
465 GtkWidget *
gdict_strategy_chooser_new_with_context(GdictContext * context)466 gdict_strategy_chooser_new_with_context (GdictContext *context)
467 {
468   g_return_val_if_fail (GDICT_IS_CONTEXT (context), NULL);
469 
470   return g_object_new (GDICT_TYPE_STRATEGY_CHOOSER,
471                        "context", context,
472                        NULL);
473 }
474 
475 /**
476  * gdict_strategy_chooser_get_context:
477  * @chooser: a #GdictStrategyChooser
478  *
479  * Retrieves the #GdictContext used by @chooser.
480  *
481  * Return value: (transfer none): a #GdictContext
482  *
483  * Since:
484  */
485 GdictContext *
gdict_strategy_chooser_get_context(GdictStrategyChooser * chooser)486 gdict_strategy_chooser_get_context (GdictStrategyChooser *chooser)
487 {
488   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), NULL);
489 
490   return chooser->priv->context;
491 }
492 
493 /**
494  * gdict_strategy_chooser_set_context:
495  * @chooser: a #GdictStrategyChooser
496  * @context: (nullable): a #GdictContext, or %NULL to unset the context
497  *
498  * Sets the #GdictContext to be used by @chooser to retrieve the
499  * list of matching strategies.
500  *
501  * Since: 0.9
502  */
503 void
gdict_strategy_chooser_set_context(GdictStrategyChooser * chooser,GdictContext * context)504 gdict_strategy_chooser_set_context (GdictStrategyChooser *chooser,
505 				    GdictContext         *context)
506 {
507   g_return_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser));
508   g_return_if_fail (context == NULL || GDICT_IS_CONTEXT (context));
509 
510   set_gdict_context (chooser, context);
511 
512   g_object_notify (G_OBJECT (chooser), "context");
513 }
514 
515 /**
516  * gdict_strategy_chooser_get_strategies:
517  * @chooser: a #GdictStrategyChooser
518  * @length: return location for the length of the returned string list
519  *
520  * Retrieves the list of matching strategies available.
521  *
522  * Return value: (transfer full): a string vector containing the names
523  *   of the matching strategies. Use g_strfreev() to deallocate the memory
524  *   when done
525  *
526  * Since:0.9
527  */
528 gchar **
gdict_strategy_chooser_get_strategies(GdictStrategyChooser * chooser,gsize * length)529 gdict_strategy_chooser_get_strategies (GdictStrategyChooser  *chooser,
530                                        gsize                 *length)
531 {
532   GdictStrategyChooserPrivate *priv;
533   GtkTreeIter iter;
534   gchar **retval;
535   gsize i;
536 
537   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), NULL);
538 
539   priv = chooser->priv;
540 
541   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter))
542     return NULL;
543 
544   i = 0;
545   retval = g_new (gchar*, priv->results);
546 
547   do
548     {
549       gchar *strat_name;
550 
551       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
552                           STRAT_COLUMN_NAME, &strat_name,
553                           -1);
554 
555       retval[i++] = strat_name;
556     }
557   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter));
558 
559   retval[i] = NULL;
560 
561   if (length)
562     *length = i;
563 
564   return retval;
565 }
566 
567 /**
568  * gdict_strategy_chooser_has_strategy:
569  * @chooser: a #GdictStrategyChooser
570  * @strategy: a strategy name
571  *
572  * Checks whether @strategy is available in the list of matching
573  * strategies displayed by @chooser.
574  *
575  * Return value: %TRUE if the strategy was found, %FALSE otherwise
576  *
577  * Since: 0.9
578  */
579 gboolean
gdict_strategy_chooser_has_strategy(GdictStrategyChooser * chooser,const gchar * strategy)580 gdict_strategy_chooser_has_strategy (GdictStrategyChooser *chooser,
581 				     const gchar          *strategy)
582 {
583   GdictStrategyChooserPrivate *priv;
584   GtkTreeIter iter;
585   gboolean retval;
586 
587   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), FALSE);
588   g_return_val_if_fail (strategy != NULL, FALSE);
589 
590   priv = chooser->priv;
591 
592   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter))
593     return FALSE;
594 
595   retval = FALSE;
596 
597   do
598     {
599       gchar *strat_name;
600 
601       gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
602                           STRAT_COLUMN_NAME, &strat_name,
603                           -1);
604 
605       if (strcmp (strat_name, strategy) == 0)
606         {
607           retval = TRUE;
608           g_free (strat_name);
609           break;
610         }
611 
612       g_free (strat_name);
613     }
614   while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter));
615 
616   return retval;
617 }
618 
619 /**
620  * gdict_strategy_chooser_count_strategies:
621  * @chooser: a #GdictStrategyChooser
622  *
623  * Returns the number of strategies found.
624  *
625  * Return value: the number of strategies or -1 if case of error
626  *
627  * Since:
628  */
629 gint
gdict_strategy_chooser_count_strategies(GdictStrategyChooser * chooser)630 gdict_strategy_chooser_count_strategies (GdictStrategyChooser *chooser)
631 {
632   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), -1);
633 
634   return chooser->priv->results;
635 }
636 
637 static void
lookup_start_cb(GdictContext * context,gpointer user_data)638 lookup_start_cb (GdictContext *context,
639 		 gpointer      user_data)
640 {
641   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (user_data);
642   GdictStrategyChooserPrivate *priv = chooser->priv;
643 
644   if (!priv->busy_cursor)
645     {
646       GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (chooser));
647       priv->busy_cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
648     }
649 
650   if (gtk_widget_get_window (GTK_WIDGET (chooser)))
651     gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (chooser)), priv->busy_cursor);
652 
653   priv->is_searching = TRUE;
654 }
655 
656 static void
lookup_end_cb(GdictContext * context,gpointer user_data)657 lookup_end_cb (GdictContext *context,
658 	       gpointer      user_data)
659 {
660   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (user_data);
661   GdictStrategyChooserPrivate *priv = chooser->priv;
662 
663   if (gtk_widget_get_window (GTK_WIDGET (chooser)))
664     gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (chooser)), NULL);
665 
666   priv->is_searching = FALSE;
667 }
668 
669 static void
strategy_found_cb(GdictContext * context,GdictStrategy * strategy,gpointer user_data)670 strategy_found_cb (GdictContext  *context,
671 		   GdictStrategy *strategy,
672 		   gpointer       user_data)
673 {
674   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (user_data);
675   GdictStrategyChooserPrivate *priv = chooser->priv;
676   const gchar *name, *description;
677   GtkTreeIter iter;
678   gint weight = PANGO_WEIGHT_NORMAL;
679 
680   name = gdict_strategy_get_name (strategy);
681   description = gdict_strategy_get_description (strategy);
682 
683   GDICT_NOTE (CHOOSER, "STRATEGY: `%s' (`%s')",
684               name,
685               description);
686 
687   if (priv->current_strat && !strcmp (priv->current_strat, name))
688     weight = PANGO_WEIGHT_BOLD;
689 
690   gtk_list_store_append (priv->store, &iter);
691   gtk_list_store_set (priv->store, &iter,
692 		      STRAT_COLUMN_TYPE, STRATEGY_NAME,
693 		      STRAT_COLUMN_NAME, name,
694 		      STRAT_COLUMN_DESCRIPTION, description,
695                       STRAT_COLUMN_CURRENT, weight,
696 		      -1);
697 
698   priv->results += 1;
699 }
700 
701 static void
error_cb(GdictContext * context,const GError * error,gpointer user_data)702 error_cb (GdictContext *context,
703           const GError *error,
704 	  gpointer      user_data)
705 {
706   GdictStrategyChooser *chooser = GDICT_STRATEGY_CHOOSER (user_data);
707 
708   if (gtk_widget_get_window (GTK_WIDGET (chooser)))
709     gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (chooser)), NULL);
710 
711   chooser->priv->is_searching = FALSE;
712   chooser->priv->results = 0;
713 }
714 
715 /**
716  * gdict_strategy_chooser_refresh:
717  * @chooser: a #GdictStrategyChooser
718  *
719  * Reloads the list of available strategies.
720  *
721  * Since: 0.10
722  */
723 void
gdict_strategy_chooser_refresh(GdictStrategyChooser * chooser)724 gdict_strategy_chooser_refresh (GdictStrategyChooser *chooser)
725 {
726   GdictStrategyChooserPrivate *priv;
727   GError *db_error;
728 
729   g_return_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser));
730 
731   priv = chooser->priv;
732 
733   if (!priv->context)
734     {
735       g_warning ("Attempting to retrieve the available strategies, but "
736 		 "no GdictContext has been set.  Use gdict_strategy_chooser_set_context() "
737 		 "before invoking gdict_strategy_chooser_refresh().");
738       return;
739     }
740 
741   if (priv->is_searching)
742     return;
743 
744   gdict_strategy_chooser_clear (chooser);
745 
746   if (!priv->start_id)
747     {
748       priv->start_id = g_signal_connect (priv->context, "lookup-start",
749 		      			 G_CALLBACK (lookup_start_cb),
750 					 chooser);
751       priv->match_id = g_signal_connect (priv->context, "strategy-found",
752 		      			 G_CALLBACK (strategy_found_cb),
753 					 chooser);
754       priv->end_id = g_signal_connect (priv->context, "lookup-end",
755 		      		       G_CALLBACK (lookup_end_cb),
756 				       chooser);
757     }
758 
759   if (!priv->error_id)
760     priv->error_id = g_signal_connect (priv->context, "error",
761 		    		       G_CALLBACK (error_cb),
762 				       chooser);
763 
764   db_error = NULL;
765   gdict_context_lookup_strategies (priv->context, &db_error);
766   if (db_error)
767     {
768       GtkTreeIter iter;
769 
770       gtk_list_store_append (priv->store, &iter);
771       gtk_list_store_set (priv->store, &iter,
772 		      	  STRAT_COLUMN_TYPE, STRATEGY_ERROR,
773 			  STRAT_COLUMN_NAME, _("Error while matching"),
774 			  STRAT_COLUMN_DESCRIPTION, NULL,
775 			  -1);
776 
777       g_warning ("Error while retrieving strategies: %s",
778                  db_error->message);
779 
780       g_error_free (db_error);
781     }
782 }
783 
784 /**
785  * gdict_strategy_chooser_clear:
786  * @chooser: a #GdictStrategyChooser
787  *
788  * Clears @chooser.
789  *
790  * Since: 0.10
791  */
792 void
gdict_strategy_chooser_clear(GdictStrategyChooser * chooser)793 gdict_strategy_chooser_clear (GdictStrategyChooser *chooser)
794 {
795   GdictStrategyChooserPrivate *priv;
796 
797   g_return_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser));
798 
799   priv = chooser->priv;
800 
801   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview), NULL);
802 
803   gtk_list_store_clear (priv->store);
804   priv->results = 0;
805 
806   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
807 		  	   GTK_TREE_MODEL (priv->store));
808 }
809 
810 typedef struct
811 {
812   gchar *strat_name;
813   GdictStrategyChooser *chooser;
814 
815   guint found       : 1;
816   guint do_select   : 1;
817   guint do_activate : 1;
818 } SelectData;
819 
820 static gboolean
scan_for_strat_name(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)821 scan_for_strat_name (GtkTreeModel *model,
822                   GtkTreePath  *path,
823                   GtkTreeIter  *iter,
824                   gpointer      user_data)
825 {
826   SelectData *select_data = user_data;
827   gchar *strat_name = NULL;
828 
829   if (!select_data)
830     return TRUE;
831 
832   gtk_tree_model_get (model, iter, STRAT_COLUMN_NAME, &strat_name, -1);
833   if (!strat_name)
834     return FALSE;
835 
836   if (strcmp (strat_name, select_data->strat_name) == 0)
837     {
838       GtkTreeView *tree_view;
839       GtkTreeSelection *selection;
840 
841       select_data->found = TRUE;
842 
843       tree_view = GTK_TREE_VIEW (select_data->chooser->priv->treeview);
844       if (select_data->do_activate)
845         {
846           GtkTreeViewColumn *column;
847 
848           gtk_list_store_set (GTK_LIST_STORE (model), iter,
849                               STRAT_COLUMN_CURRENT, PANGO_WEIGHT_BOLD,
850                               -1);
851 
852           column = gtk_tree_view_get_column (tree_view, 2);
853           gtk_tree_view_row_activated (tree_view, path, column);
854         }
855 
856       selection = gtk_tree_view_get_selection (tree_view);
857       if (select_data->do_select)
858         gtk_tree_selection_select_path (selection, path);
859       else
860         gtk_tree_selection_unselect_path (selection, path);
861     }
862   else
863     {
864       gtk_list_store_set (GTK_LIST_STORE (model), iter,
865                           STRAT_COLUMN_CURRENT, PANGO_WEIGHT_NORMAL,
866                           -1);
867     }
868 
869   g_free (strat_name);
870 
871   return FALSE;
872 }
873 
874 /**
875  * gdict_strategy_chooser_select_strategy:
876  * @chooser: a #GdictStrategyChooser
877  * @strat_name: the name of the strategy to select
878  *
879  * Selects @strat_name, if available.
880  *
881  * Return value: %TRUE if the matching strategy was found and selected
882  *
883  * Since: 0.10
884  */
885 gboolean
gdict_strategy_chooser_select_strategy(GdictStrategyChooser * chooser,const gchar * strat_name)886 gdict_strategy_chooser_select_strategy (GdictStrategyChooser *chooser,
887                                         const gchar          *strat_name)
888 {
889   GdictStrategyChooserPrivate *priv;
890   SelectData data;
891   gboolean retval;
892 
893   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), FALSE);
894   g_return_val_if_fail (strat_name != NULL, FALSE);
895 
896   priv = chooser->priv;
897 
898   data.strat_name = g_strdup (strat_name);
899   data.chooser = chooser;
900   data.found = FALSE;
901   data.do_select = TRUE;
902   data.do_activate = FALSE;
903 
904   gtk_tree_model_foreach (GTK_TREE_MODEL (priv->store),
905                           scan_for_strat_name,
906                           &data);
907 
908   retval = data.found;
909 
910   g_free (data.strat_name);
911 
912   return retval;
913 }
914 
915 /**
916  * gdict_strategy_chooser_unselect_strategy:
917  * @chooser: a #GdictStrategyChooser
918  * @strat_name: the name of the strategy to unselect
919  *
920  * Unselects @strat_name from the list.
921  *
922  * Return value: %TRUE if the matching strategy was found and successfully
923  *   unselected
924  *
925  * Since: 0.10
926  */
927 gboolean
gdict_strategy_chooser_unselect_strategy(GdictStrategyChooser * chooser,const gchar * strat_name)928 gdict_strategy_chooser_unselect_strategy (GdictStrategyChooser *chooser,
929                                           const gchar          *strat_name)
930 {
931   GdictStrategyChooserPrivate *priv;
932   SelectData data;
933   gboolean retval;
934 
935   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), FALSE);
936   g_return_val_if_fail (strat_name != NULL, FALSE);
937 
938   priv = chooser->priv;
939 
940   data.strat_name = g_strdup (strat_name);
941   data.chooser = chooser;
942   data.found = FALSE;
943   data.do_select = FALSE;
944   data.do_activate = FALSE;
945 
946   gtk_tree_model_foreach (GTK_TREE_MODEL (priv->store),
947                           scan_for_strat_name,
948                           &data);
949 
950   retval = data.found;
951 
952   g_free (data.strat_name);
953 
954   return retval;
955 }
956 
957 /**
958  * gdict_strategy_chooser_set_current_strategy:
959  * @chooser: a #GdictStrategyChooser
960  * @strat_name: the name of the matching strategy
961  *
962  * Sets @strat_name as the current matching strategy.
963  *
964  * Return value: %TRUE if the matching strategy was found
965  *
966  * Since: 0.10
967  */
968 gboolean
gdict_strategy_chooser_set_current_strategy(GdictStrategyChooser * chooser,const gchar * strat_name)969 gdict_strategy_chooser_set_current_strategy (GdictStrategyChooser *chooser,
970                                              const gchar          *strat_name)
971 {
972   GdictStrategyChooserPrivate *priv;
973   SelectData data;
974   gboolean retval;
975 
976   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), FALSE);
977   g_return_val_if_fail (strat_name != NULL, FALSE);
978 
979   priv = chooser->priv;
980 
981   if (priv->current_strat && !strcmp (priv->current_strat, strat_name))
982     return TRUE;
983 
984   data.strat_name = g_strdup (strat_name);
985   data.chooser = chooser;
986   data.found = FALSE;
987   data.do_select = TRUE;
988   data.do_activate = TRUE;
989 
990   gtk_tree_model_foreach (GTK_TREE_MODEL (priv->store),
991                           scan_for_strat_name,
992                           &data);
993 
994   retval = data.found;
995 
996   if (data.found)
997     {
998       g_free (priv->current_strat);
999       priv->current_strat = data.strat_name;
1000     }
1001   else
1002     g_free (data.strat_name);
1003 
1004   return retval;
1005 }
1006 
1007 /**
1008  * gdict_strategy_chooser_get_current_strategy:
1009  * @chooser: a #GdictStrategyChooser
1010  *
1011  * Retrieves the current matching strategy.
1012  *
1013  * Return value: a newly allocated string containing the name of
1014  *   the current matching strategy
1015  *
1016  * Since: 0.10
1017  */
1018 gchar *
gdict_strategy_chooser_get_current_strategy(GdictStrategyChooser * chooser)1019 gdict_strategy_chooser_get_current_strategy (GdictStrategyChooser *chooser)
1020 {
1021   GdictStrategyChooserPrivate *priv;
1022   GtkTreeSelection *selection;
1023   GtkTreeModel *model;
1024   GtkTreeIter iter;
1025   gchar *retval = NULL;
1026 
1027   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), NULL);
1028 
1029   priv = chooser->priv;
1030 
1031   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1032   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1033     return NULL;
1034 
1035   gtk_tree_model_get (model, &iter, STRAT_COLUMN_NAME, &retval, -1);
1036 
1037   g_free (priv->current_strat);
1038   priv->current_strat = g_strdup (retval);
1039 
1040   return retval;
1041 }
1042 
1043 /**
1044  * gdict_strategy_chooser_add_button:
1045  * @chooser: a #GdictStrategyChooser
1046  * @button_text: text of the button
1047  *
1048  * Creates a new button and packs it into the #GdictStrategyChooser
1049  * "action area".
1050  *
1051  * Return value: (transfer none): the packed #GtkButton
1052  *
1053  * Since: 0.10
1054  */
1055 GtkWidget *
gdict_strategy_chooser_add_button(GdictStrategyChooser * chooser,const gchar * button_text)1056 gdict_strategy_chooser_add_button (GdictStrategyChooser *chooser,
1057                                    const gchar          *button_text)
1058 {
1059   GdictStrategyChooserPrivate *priv;
1060   GtkWidget *button;
1061 
1062   g_return_val_if_fail (GDICT_IS_STRATEGY_CHOOSER (chooser), NULL);
1063   g_return_val_if_fail (button_text != NULL, NULL);
1064 
1065   priv = chooser->priv;
1066 
1067   button = gtk_button_new_with_label (button_text);
1068 
1069   gtk_widget_set_can_default (button, TRUE);
1070 
1071   gtk_widget_show (button);
1072 
1073   gtk_box_pack_end (GTK_BOX (priv->buttons_box), button, FALSE, TRUE, 0);
1074 
1075   return button;
1076 }
1077