1 /* gdict-pref-dialog.c - preferences dialog
2 *
3 * This file is part of GNOME Dictionary
4 *
5 * Copyright (C) 2005 Emmanuele Bassi
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <sys/types.h>
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 #include <glib/gi18n.h>
33
34 #include <gio/gio.h>
35
36 #include "gdict-source-dialog.h"
37 #include "gdict-pref-dialog.h"
38 #include "gdict-common.h"
39
40 #define GDICT_PREFERENCES_UI "/org/gnome/Dictionary/gdict-pref-dialog.ui"
41
42 /*******************
43 * GdictPrefDialog *
44 *******************/
45
46 static GtkWidget *global_dialog = NULL;
47
48 enum
49 {
50 SOURCES_ACTIVE_COLUMN = 0,
51 SOURCES_NAME_COLUMN,
52 SOURCES_DESCRIPTION_COLUMN,
53
54 SOURCES_N_COLUMNS
55 };
56
57 struct _GdictPrefDialog
58 {
59 GtkDialog parent_instance;
60
61 GtkBuilder *builder;
62
63 GSettings *settings;
64
65 gchar *print_font;
66 gchar *active_source;
67 GdictSourceLoader *loader;
68 GtkListStore *sources_list;
69
70 /* direct pointers to widgets */
71 GtkWidget *preferences_root;
72 GtkWidget *preferences_notebook;
73 GtkWidget *sources_treeview;
74 GtkWidget *add_button;
75 GtkWidget *remove_button;
76 GtkWidget *edit_button;
77 GtkWidget *font_button;
78 };
79
80 struct _GdictPrefDialogClass
81 {
82 GtkDialogClass parent_class;
83 };
84
85 enum
86 {
87 PROP_0,
88
89 PROP_SOURCE_LOADER
90 };
91
G_DEFINE_TYPE(GdictPrefDialog,gdict_pref_dialog,GTK_TYPE_DIALOG)92 G_DEFINE_TYPE (GdictPrefDialog, gdict_pref_dialog, GTK_TYPE_DIALOG)
93
94 static gboolean
95 select_active_source_name (GtkTreeModel *model,
96 GtkTreePath *path,
97 GtkTreeIter *iter,
98 gpointer data)
99 {
100 GdictPrefDialog *dialog = GDICT_PREF_DIALOG (data);
101 gboolean is_active;
102
103 gtk_tree_model_get (model, iter,
104 SOURCES_ACTIVE_COLUMN, &is_active,
105 -1);
106 if (is_active)
107 {
108 GtkTreeSelection *selection;
109
110 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->sources_treeview));
111
112 gtk_tree_selection_select_iter (selection, iter);
113
114 return TRUE;
115 }
116
117 return FALSE;
118 }
119
120 static void
sources_view_cursor_changed_cb(GtkTreeView * tree_view,GdictPrefDialog * dialog)121 sources_view_cursor_changed_cb (GtkTreeView *tree_view,
122 GdictPrefDialog *dialog)
123 {
124 GtkTreeSelection *selection;
125 GtkTreeModel *model;
126 GtkTreeIter iter;
127 GdictSource *source;
128 gboolean is_selected;
129 gchar *name;
130
131 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->sources_treeview));
132 if (!selection)
133 return;
134
135 is_selected = gtk_tree_selection_get_selected (selection, &model, &iter);
136 if (!is_selected)
137 return;
138
139 gtk_tree_model_get (model, &iter, SOURCES_NAME_COLUMN, &name, -1);
140 if (!name)
141 return;
142 else
143 {
144 source = gdict_source_loader_get_source (dialog->loader, name);
145 gtk_widget_set_sensitive (dialog->edit_button, gdict_source_is_editable (source));
146 gtk_widget_set_sensitive (dialog->remove_button, gdict_source_is_editable (source));
147 g_object_unref (source);
148 }
149 }
150
151 static void
update_sources_view(GdictPrefDialog * dialog)152 update_sources_view (GdictPrefDialog *dialog)
153 {
154 const GSList *sources, *l;
155
156 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->sources_treeview), NULL);
157
158 gtk_list_store_clear (dialog->sources_list);
159
160 /* force update of the sources list */
161 gdict_source_loader_update (dialog->loader);
162
163 sources = gdict_source_loader_get_sources (dialog->loader);
164 for (l = sources; l != NULL; l = l->next)
165 {
166 GdictSource *source = GDICT_SOURCE (l->data);
167 GtkTreeIter iter;
168 const gchar *name, *description;
169 gboolean is_active = FALSE;
170
171 name = gdict_source_get_name (source);
172 description = gdict_source_get_description (source);
173 if (!description)
174 description = name;
175
176 if (strcmp (name, dialog->active_source) == 0)
177 is_active = TRUE;
178
179 gtk_list_store_append (dialog->sources_list, &iter);
180 gtk_list_store_set (dialog->sources_list, &iter,
181 SOURCES_ACTIVE_COLUMN, is_active,
182 SOURCES_NAME_COLUMN, name,
183 SOURCES_DESCRIPTION_COLUMN, description,
184 -1);
185 }
186
187 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->sources_treeview),
188 GTK_TREE_MODEL (dialog->sources_list));
189
190 /* select the currently active source name */
191 gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->sources_list),
192 select_active_source_name,
193 dialog);
194 sources_view_cursor_changed_cb (GTK_TREE_VIEW(dialog->sources_treeview), dialog);
195 }
196
197 static void
source_renderer_toggled_cb(GtkCellRendererToggle * renderer,const gchar * path,GdictPrefDialog * dialog)198 source_renderer_toggled_cb (GtkCellRendererToggle *renderer,
199 const gchar *path,
200 GdictPrefDialog *dialog)
201 {
202 GtkTreePath *treepath;
203 GtkTreeIter iter;
204 gboolean res;
205 gboolean is_active;
206 gchar *name;
207
208 treepath = gtk_tree_path_new_from_string (path);
209 res = gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->sources_list),
210 &iter,
211 treepath);
212 if (!res)
213 {
214 gtk_tree_path_free (treepath);
215
216 return;
217 }
218
219 gtk_tree_model_get (GTK_TREE_MODEL (dialog->sources_list), &iter,
220 SOURCES_NAME_COLUMN, &name,
221 SOURCES_ACTIVE_COLUMN, &is_active,
222 -1);
223 if (!is_active && name != NULL)
224 {
225 g_free (dialog->active_source);
226 dialog->active_source = g_strdup (name);
227
228 g_settings_set_string (dialog->settings, GDICT_SETTINGS_SOURCE_KEY, dialog->active_source);
229 update_sources_view (dialog);
230
231 g_free (name);
232 }
233
234 gtk_tree_path_free (treepath);
235 }
236
237 static void
sources_view_row_activated_cb(GtkTreeView * tree_view,GtkTreePath * tree_path,GtkTreeViewColumn * tree_iter,GdictPrefDialog * dialog)238 sources_view_row_activated_cb (GtkTreeView *tree_view,
239 GtkTreePath *tree_path,
240 GtkTreeViewColumn *tree_iter,
241 GdictPrefDialog *dialog)
242 {
243 GtkWidget *edit_dialog;
244 gchar *source_name;
245 GtkTreeModel *model;
246 GtkTreeIter iter;
247
248 model = gtk_tree_view_get_model (tree_view);
249 if (!model)
250 return;
251
252 if (!gtk_tree_model_get_iter (model, &iter, tree_path))
253 return;
254
255 gtk_tree_model_get (model, &iter, SOURCES_NAME_COLUMN, &source_name, -1);
256 if (!source_name)
257 return;
258
259 edit_dialog = gdict_source_dialog_new (GTK_WINDOW (dialog),
260 _("View Dictionary Source"),
261 GDICT_SOURCE_DIALOG_VIEW,
262 dialog->loader,
263 source_name);
264 gtk_dialog_run (GTK_DIALOG (edit_dialog));
265
266 gtk_widget_destroy (edit_dialog);
267 g_free (source_name);
268
269 update_sources_view (dialog);
270 }
271
272 static void
build_sources_view(GdictPrefDialog * dialog)273 build_sources_view (GdictPrefDialog *dialog)
274 {
275 GtkTreeViewColumn *column;
276 GtkCellRenderer *renderer;
277
278 if (dialog->sources_list)
279 return;
280
281 dialog->sources_list = gtk_list_store_new (SOURCES_N_COLUMNS,
282 G_TYPE_BOOLEAN, /* active */
283 G_TYPE_STRING, /* name */
284 G_TYPE_STRING /* description */);
285 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->sources_list),
286 SOURCES_DESCRIPTION_COLUMN,
287 GTK_SORT_ASCENDING);
288
289 renderer = gtk_cell_renderer_toggle_new ();
290 gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer), TRUE);
291 g_signal_connect (renderer, "toggled",
292 G_CALLBACK (source_renderer_toggled_cb),
293 dialog);
294
295 column = gtk_tree_view_column_new_with_attributes ("active",
296 renderer,
297 "active", SOURCES_ACTIVE_COLUMN,
298 NULL);
299 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->sources_treeview), column);
300
301 renderer = gtk_cell_renderer_text_new ();
302 column = gtk_tree_view_column_new_with_attributes ("description",
303 renderer,
304 "text", SOURCES_DESCRIPTION_COLUMN,
305 NULL);
306 gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->sources_treeview), column);
307
308 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->sources_treeview), FALSE);
309 gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->sources_treeview),
310 GTK_TREE_MODEL (dialog->sources_list));
311
312 g_signal_connect (dialog->sources_treeview, "row-activated",
313 G_CALLBACK (sources_view_row_activated_cb),
314 dialog);
315 g_signal_connect (dialog->sources_treeview, "cursor-changed",
316 G_CALLBACK (sources_view_cursor_changed_cb),
317 dialog);
318 }
319
320 static void
source_add_clicked_cb(GdictPrefDialog * dialog)321 source_add_clicked_cb (GdictPrefDialog *dialog)
322 {
323 GtkWidget *add_dialog;
324
325 add_dialog = gdict_source_dialog_new (GTK_WINDOW (dialog),
326 _("Add Dictionary Source"),
327 GDICT_SOURCE_DIALOG_CREATE,
328 dialog->loader,
329 NULL);
330
331 gtk_dialog_run (GTK_DIALOG (add_dialog));
332
333 gtk_widget_destroy (add_dialog);
334
335 update_sources_view (dialog);
336 }
337
338 static void
source_remove_clicked_cb(GdictPrefDialog * dialog)339 source_remove_clicked_cb (GdictPrefDialog *dialog)
340 {
341 GtkTreeSelection *selection;
342 GtkTreeModel *model;
343 GtkTreeIter iter;
344 gboolean is_selected;
345 gchar *name, *description;
346
347 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->sources_treeview));
348 if (!selection)
349 return;
350
351 is_selected = gtk_tree_selection_get_selected (selection, &model, &iter);
352 if (!is_selected)
353 return;
354
355 gtk_tree_model_get (model, &iter,
356 SOURCES_NAME_COLUMN, &name,
357 SOURCES_DESCRIPTION_COLUMN, &description,
358 -1);
359 if (!name)
360 return;
361 else
362 {
363 GtkWidget *confirm_dialog;
364 gint response;
365
366 confirm_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
367 GTK_DIALOG_DESTROY_WITH_PARENT,
368 GTK_MESSAGE_WARNING,
369 GTK_BUTTONS_NONE,
370 _("Remove “%s”?"), description);
371 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (confirm_dialog),
372 _("This will permanently remove the "
373 "dictionary source from the list."));
374
375 gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), _("_Cancel"), GTK_RESPONSE_CANCEL);
376 gtk_dialog_add_button (GTK_DIALOG (confirm_dialog), _("_Remove"), GTK_RESPONSE_OK);
377
378 gtk_window_set_title (GTK_WINDOW (confirm_dialog), "");
379
380 response = gtk_dialog_run (GTK_DIALOG (confirm_dialog));
381 gtk_widget_destroy (confirm_dialog);
382
383 if (response == GTK_RESPONSE_CANCEL)
384 goto out;
385 }
386
387 if (gdict_source_loader_remove_source (dialog->loader, name))
388 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
389 else
390 {
391 GtkWidget *error_dialog;
392 gchar *message;
393
394 message = g_strdup_printf (_("Unable to remove source “%s”"),
395 description);
396
397 error_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
398 GTK_DIALOG_DESTROY_WITH_PARENT,
399 GTK_MESSAGE_ERROR,
400 GTK_BUTTONS_OK,
401 "%s", message);
402 gtk_window_set_title (GTK_WINDOW (error_dialog), "");
403
404 gtk_dialog_run (GTK_DIALOG (error_dialog));
405
406 gtk_widget_destroy (error_dialog);
407 }
408
409 out:
410 g_free (name);
411 g_free (description);
412
413 update_sources_view (dialog);
414 }
415
416 static void
source_edit_clicked_cb(GdictPrefDialog * dialog)417 source_edit_clicked_cb (GdictPrefDialog *dialog)
418 {
419 GtkTreeSelection *selection;
420 GtkTreeModel *model;
421 GtkTreeIter iter;
422 gboolean is_selected;
423 gchar *name;
424
425 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->sources_treeview));
426 if (!selection)
427 return;
428
429 is_selected = gtk_tree_selection_get_selected (selection, &model, &iter);
430 if (!is_selected)
431 return;
432
433 gtk_tree_model_get (model, &iter, SOURCES_NAME_COLUMN, &name, -1);
434 if (!name)
435 return;
436 else
437 {
438 GtkWidget *edit_dialog;
439
440 edit_dialog = gdict_source_dialog_new (GTK_WINDOW (dialog),
441 _("Edit Dictionary Source"),
442 GDICT_SOURCE_DIALOG_EDIT,
443 dialog->loader,
444 name);
445 gtk_dialog_run (GTK_DIALOG (edit_dialog));
446
447 gtk_widget_destroy (edit_dialog);
448 }
449
450 g_free (name);
451
452 update_sources_view (dialog);
453 }
454
455 static void
set_source_loader(GdictPrefDialog * dialog,GdictSourceLoader * loader)456 set_source_loader (GdictPrefDialog *dialog,
457 GdictSourceLoader *loader)
458 {
459 if (!dialog->sources_list)
460 return;
461
462 if (dialog->loader)
463 g_object_unref (dialog->loader);
464
465 dialog->loader = g_object_ref (loader);
466
467 update_sources_view (dialog);
468 }
469
470 static void
font_button_font_set_cb(GdictPrefDialog * dialog,GtkFontButton * font_button)471 font_button_font_set_cb (GdictPrefDialog *dialog,
472 GtkFontButton *font_button)
473 {
474 const char *font;
475
476 font = gtk_font_chooser_get_font (GTK_FONT_CHOOSER (font_button));
477 if (!font || font[0] == '\0')
478 return;
479
480 if (g_strcmp0 (dialog->print_font, font) == 0)
481 return;
482
483 g_free (dialog->print_font);
484 dialog->print_font = g_strdup (font);
485
486 g_settings_set_string (dialog->settings, GDICT_SETTINGS_PRINT_FONT_KEY, dialog->print_font);
487 }
488
489 static void
gdict_pref_dialog_finalize(GObject * object)490 gdict_pref_dialog_finalize (GObject *object)
491 {
492 GdictPrefDialog *dialog = GDICT_PREF_DIALOG (object);
493
494 g_clear_object (&dialog->settings);
495 g_clear_object (&dialog->loader);
496
497 g_free (dialog->active_source);
498
499 G_OBJECT_CLASS (gdict_pref_dialog_parent_class)->finalize (object);
500 }
501
502 static void
gdict_pref_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)503 gdict_pref_dialog_set_property (GObject *object,
504 guint prop_id,
505 const GValue *value,
506 GParamSpec *pspec)
507 {
508 GdictPrefDialog *dialog = GDICT_PREF_DIALOG (object);
509
510 switch (prop_id)
511 {
512 case PROP_SOURCE_LOADER:
513 set_source_loader (dialog, g_value_get_object (value));
514 break;
515
516 default:
517 break;
518 }
519 }
520
521 static void
gdict_pref_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)522 gdict_pref_dialog_get_property (GObject *object,
523 guint prop_id,
524 GValue *value,
525 GParamSpec *pspec)
526 {
527 GdictPrefDialog *dialog = GDICT_PREF_DIALOG (object);
528
529 switch (prop_id)
530 {
531 case PROP_SOURCE_LOADER:
532 g_value_set_object (value, dialog->loader);
533 break;
534
535 default:
536 break;
537 }
538 }
539
540 static void
gdict_pref_dialog_class_init(GdictPrefDialogClass * klass)541 gdict_pref_dialog_class_init (GdictPrefDialogClass *klass)
542 {
543 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
544 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
545
546 gobject_class->set_property = gdict_pref_dialog_set_property;
547 gobject_class->get_property = gdict_pref_dialog_get_property;
548 gobject_class->finalize = gdict_pref_dialog_finalize;
549
550 g_object_class_install_property (gobject_class,
551 PROP_SOURCE_LOADER,
552 g_param_spec_object ("source-loader",
553 "Source Loader",
554 "The GdictSourceLoader used by the application",
555 GDICT_TYPE_SOURCE_LOADER,
556 G_PARAM_READWRITE |
557 G_PARAM_CONSTRUCT_ONLY |
558 G_PARAM_STATIC_STRINGS));
559
560 gtk_widget_class_set_template_from_resource (widget_class, GDICT_PREFERENCES_UI);
561
562 gtk_widget_class_bind_template_child (widget_class, GdictPrefDialog, preferences_notebook);
563 gtk_widget_class_bind_template_child (widget_class, GdictPrefDialog, sources_treeview);
564 gtk_widget_class_bind_template_child (widget_class, GdictPrefDialog, add_button);
565 gtk_widget_class_bind_template_child (widget_class, GdictPrefDialog, remove_button);
566 gtk_widget_class_bind_template_child (widget_class, GdictPrefDialog, edit_button);
567 gtk_widget_class_bind_template_child (widget_class, GdictPrefDialog, font_button);
568
569 gtk_widget_class_bind_template_callback (widget_class, source_add_clicked_cb);
570 gtk_widget_class_bind_template_callback (widget_class, source_remove_clicked_cb);
571 gtk_widget_class_bind_template_callback (widget_class, source_edit_clicked_cb);
572 gtk_widget_class_bind_template_callback (widget_class, font_button_font_set_cb);
573 }
574
575 static void
gdict_pref_dialog_init(GdictPrefDialog * dialog)576 gdict_pref_dialog_init (GdictPrefDialog *dialog)
577 {
578 gchar *font;
579
580 gtk_widget_init_template (GTK_WIDGET (dialog));
581
582 dialog->settings = g_settings_new (GDICT_SETTINGS_SCHEMA);
583 dialog->active_source = g_settings_get_string (dialog->settings, GDICT_SETTINGS_SOURCE_KEY);
584
585 build_sources_view (dialog);
586
587 font = g_settings_get_string (dialog->settings, GDICT_SETTINGS_PRINT_FONT_KEY);
588 gtk_font_chooser_set_font (GTK_FONT_CHOOSER (dialog->font_button), font);
589 g_free (font);
590
591 gtk_widget_show_all (dialog->preferences_notebook);
592 }
593
594 void
gdict_show_pref_dialog(GtkWidget * parent,const gchar * title,GdictSourceLoader * loader)595 gdict_show_pref_dialog (GtkWidget *parent,
596 const gchar *title,
597 GdictSourceLoader *loader)
598 {
599 GtkWidget *dialog;
600
601 g_return_if_fail (GTK_IS_WIDGET (parent));
602 g_return_if_fail (GDICT_IS_SOURCE_LOADER (loader));
603
604 if (parent != NULL)
605 dialog = g_object_get_data (G_OBJECT (parent), "gdict-pref-dialog");
606 else
607 dialog = global_dialog;
608
609 if (dialog == NULL)
610 {
611 dialog = g_object_new (GDICT_TYPE_PREF_DIALOG,
612 "source-loader", loader,
613 "title", title,
614 "use-header-bar", 1,
615 NULL);
616
617 g_object_ref_sink (dialog);
618
619 g_signal_connect (dialog, "delete-event",
620 G_CALLBACK (gtk_widget_hide_on_delete),
621 NULL);
622
623 if (parent != NULL && GTK_IS_WINDOW (parent))
624 {
625 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent));
626 gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
627 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
628 g_object_set_data_full (G_OBJECT (parent), "gdict-pref-dialog",
629 dialog,
630 g_object_unref);
631 }
632 else
633 global_dialog = dialog;
634 }
635
636 gtk_window_set_screen (GTK_WINDOW (dialog), gtk_widget_get_screen (parent));
637 gtk_window_present (GTK_WINDOW (dialog));
638 }
639