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