1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * settings.c
4  * Copyright (C) John Stebbins 2008-2021 <stebbins@stebbins>
5  *
6  * settings.c is free software.
7  *
8  * You may redistribute it and/or modify it under the terms of the
9  * GNU General Public License version 2, as published by the Free Software
10  * Foundation.
11  *
12  * settings.c 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.
15  * See the GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with main.c.  If not, write to:
19  *  The Free Software Foundation, Inc.,
20  *  51 Franklin Street, Fifth Floor
21  *  Boston, MA  02110-1301, USA.
22  */
23 
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <glib.h>
27 #include <glib/gstdio.h>
28 #include <string.h>
29 #include "ghbcompat.h"
30 #include "settings.h"
31 #include "hb-backend.h"
32 #include "values.h"
33 
34 void dump_settings(GhbValue *settings);
35 void ghb_pref_audio_init(signal_user_data_t *ud);
36 
37 GObject*
debug_get_object(GtkBuilder * b,const gchar * n)38 debug_get_object(GtkBuilder* b, const gchar *n)
39 {
40     g_message("name %s\n", n);
41     return gtk_builder_get_object(b, n);
42 }
43 
44 gint
ghb_settings_combo_int(const GhbValue * settings,const gchar * key)45 ghb_settings_combo_int(const GhbValue *settings, const gchar *key)
46 {
47     return ghb_lookup_combo_int(key, ghb_dict_get_value(settings, key));
48 }
49 
50 gdouble
ghb_settings_combo_double(const GhbValue * settings,const gchar * key)51 ghb_settings_combo_double(const GhbValue *settings, const gchar *key)
52 {
53     return ghb_lookup_combo_double(key, ghb_dict_get_value(settings, key));
54 }
55 
56 gchar*
ghb_settings_combo_option(const GhbValue * settings,const gchar * key)57 ghb_settings_combo_option(const GhbValue *settings, const gchar *key)
58 {
59     return ghb_lookup_combo_option(key, ghb_dict_get_value(settings, key));
60 }
61 
62 // Map widget names to setting keys
63 // Widgets that map to settings have names
64 // of this format: s_<setting key>
65 const gchar*
ghb_get_setting_key(GtkWidget * widget)66 ghb_get_setting_key(GtkWidget *widget)
67 {
68     const gchar *name;
69 
70     g_debug("get_setting_key ()\n");
71     if (widget == NULL) return NULL;
72     name = gtk_buildable_get_name(GTK_BUILDABLE(widget));
73 
74     if (name == NULL || !strncmp(name, "Gtk", 3))
75     {
76         name = gtk_widget_get_name(widget);
77     }
78     if (name == NULL)
79     {
80         // Bad widget pointer?  Should never happen.
81         g_debug("Bad widget\n");
82         return NULL;
83     }
84     return name;
85 }
86 
87 GhbValue*
ghb_widget_value(GtkWidget * widget)88 ghb_widget_value(GtkWidget *widget)
89 {
90     GhbValue *value = NULL;
91     const gchar *name;
92     GType type;
93 
94     if (widget == NULL)
95     {
96         g_debug("NULL widget\n");
97         return NULL;
98     }
99 
100     type = G_OBJECT_TYPE(widget);
101     name = ghb_get_setting_key(widget);
102     if (type == GTK_TYPE_ENTRY)
103     {
104         const gchar *str = ghb_editable_get_text(widget);
105         value = ghb_string_value_new(str);
106     }
107     else if (type == GTK_TYPE_RADIO_BUTTON)
108     {
109         gboolean bval;
110 #if !GTK_CHECK_VERSION(3, 90, 0)
111         bval = gtk_toggle_button_get_inconsistent(GTK_TOGGLE_BUTTON(widget));
112         if (bval)
113         {
114             value = ghb_bool_value_new(FALSE);
115         }
116         else
117 #endif
118         {
119             bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
120             value = ghb_bool_value_new(bval);
121         }
122     }
123     else if (type == GTK_TYPE_CHECK_BUTTON)
124     {
125         gboolean bval;
126         bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
127         value = ghb_bool_value_new(bval);
128     }
129     else if (type == GTK_TYPE_TOGGLE_TOOL_BUTTON)
130     {
131         gboolean bval;
132         bval = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(widget));
133         value = ghb_bool_value_new(bval);
134     }
135     else if (type == GTK_TYPE_TOGGLE_BUTTON)
136     {
137         gboolean bval;
138         bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
139         value = ghb_bool_value_new(bval);
140     }
141     else if (type == GTK_TYPE_CHECK_MENU_ITEM)
142     {
143         gboolean bval;
144         bval = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
145         value = ghb_bool_value_new(bval);
146     }
147     else if (type == GTK_TYPE_COMBO_BOX)
148     {
149         GtkTreeModel *store;
150         GtkTreeIter iter;
151         gchar *shortOpt;
152 
153         store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
154         if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter))
155         {
156             gtk_tree_model_get(store, &iter, 2, &shortOpt, -1);
157             value = ghb_string_value_new(shortOpt);
158             g_free(shortOpt);
159         }
160         else if (gtk_combo_box_get_has_entry(GTK_COMBO_BOX(widget)))
161         {
162             const gchar *str;
163             str = ghb_editable_get_text(gtk_bin_get_child(GTK_BIN(widget)));
164             if (str == NULL) str = "";
165             value = ghb_string_value_new(str);
166         }
167         else
168         {
169             value = ghb_string_value_new("");
170         }
171     }
172     else if (type == GTK_TYPE_SPIN_BUTTON)
173     {
174         double val;
175         val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));
176         value = ghb_double_value_new(val);
177     }
178     else if (type == GTK_TYPE_SCALE)
179     {
180         gdouble dval;
181         gint digits;
182 
183         digits = gtk_scale_get_digits(GTK_SCALE(widget));
184         dval = gtk_range_get_value(GTK_RANGE(widget));
185         if (digits)
186         {
187             value = ghb_double_value_new(dval);
188         }
189         else
190         {
191             value = ghb_int_value_new(dval);
192         }
193     }
194     else if (type == GTK_TYPE_SCALE_BUTTON)
195     {
196         gdouble dval;
197 
198         dval = gtk_scale_button_get_value(GTK_SCALE_BUTTON(widget));
199         value = ghb_double_value_new(dval);
200     }
201     else if (type == GTK_TYPE_TEXT_VIEW)
202     {
203         GtkTextBuffer *buffer;
204         GtkTextIter start, end;
205         gchar *str;
206 
207         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
208         gtk_text_buffer_get_bounds(buffer, &start, &end);
209         str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
210         value = ghb_string_value_new(str);
211         g_free(str);
212     }
213     else if (type == GTK_TYPE_LABEL)
214     {
215         const gchar *str;
216         str = gtk_label_get_text (GTK_LABEL(widget));
217         value = ghb_string_value_new(str);
218     }
219     else if (type == GTK_TYPE_FILE_CHOOSER_BUTTON)
220     {
221         gchar *str = NULL;
222         str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(widget));
223         if (str == NULL)
224         {
225             str = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget));
226         }
227         value = ghb_string_value_new(str);
228         if (str != NULL)
229             g_free(str);
230     }
231     else
232     {
233         g_warning("Attempt to get unknown widget type, name %s", name);
234         g_free(value);
235         value = NULL;
236     }
237     return value;
238 }
239 
240 gchar*
ghb_widget_string(GtkWidget * widget)241 ghb_widget_string(GtkWidget *widget)
242 {
243     GhbValue *value;
244     gchar *sval;
245 
246     value = ghb_widget_value(widget);
247     sval = ghb_value_get_string_xform(value);
248     ghb_value_free(&value);
249     return sval;
250 }
251 
252 gdouble
ghb_widget_double(GtkWidget * widget)253 ghb_widget_double(GtkWidget *widget)
254 {
255     GhbValue *value;
256     gdouble dval;
257 
258     value = ghb_widget_value(widget);
259     dval = ghb_value_get_double(value);
260     ghb_value_free(&value);
261     return dval;
262 }
263 
264 gint64
ghb_widget_int64(GtkWidget * widget)265 ghb_widget_int64(GtkWidget *widget)
266 {
267     GhbValue *value;
268     gint64 ival;
269 
270     value = ghb_widget_value(widget);
271     ival = ghb_value_get_int(value);
272     ghb_value_free(&value);
273     return ival;
274 }
275 
276 gint
ghb_widget_int(GtkWidget * widget)277 ghb_widget_int(GtkWidget *widget)
278 {
279     GhbValue *value;
280     gint ival;
281 
282     value = ghb_widget_value(widget);
283     ival = (gint)ghb_value_get_int(value);
284     ghb_value_free(&value);
285     return ival;
286 }
287 
288 gint
ghb_widget_boolean(GtkWidget * widget)289 ghb_widget_boolean(GtkWidget *widget)
290 {
291     GhbValue *value;
292     gboolean bval;
293 
294     value = ghb_widget_value(widget);
295     bval = ghb_value_get_bool(value);
296     ghb_value_free(&value);
297     return bval;
298 }
299 
300 void
ghb_widget_to_setting(GhbValue * settings,GtkWidget * widget)301 ghb_widget_to_setting(GhbValue *settings, GtkWidget *widget)
302 {
303     const gchar *key = NULL;
304     GhbValue *value;
305 
306     if (widget == NULL) return;
307     g_debug("ghb_widget_to_setting");
308     // Find corresponding setting
309     key = ghb_get_setting_key(widget);
310     if (key == NULL) return;
311     value = ghb_widget_value(widget);
312     if (value != NULL)
313     {
314         ghb_dict_set(settings, key, value);
315     }
316     else
317     {
318         g_debug("No value found for %s\n", key);
319     }
320 }
321 
322 void
ghb_update_widget(GtkWidget * widget,const GhbValue * value)323 ghb_update_widget(GtkWidget *widget, const GhbValue *value)
324 {
325     GType type;
326     gchar *str, *tmp;
327     gint ival;
328     gdouble dval;
329 
330     const char *name = ghb_get_setting_key(widget);
331     type = ghb_value_type(value);
332     if (type == GHB_ARRAY || type == GHB_DICT)
333         return;
334     if (value == NULL) return;
335     str = tmp = ghb_value_get_string_xform(value);
336     ival = ghb_value_get_int(value);
337     dval = ghb_value_get_double(value);
338     type = G_OBJECT_TYPE(widget);
339 
340     if (str == NULL)
341         str = "";
342 
343     if (type == GTK_TYPE_ENTRY)
344     {
345         ghb_editable_set_text(widget, str);
346     }
347     else if (type == GTK_TYPE_RADIO_BUTTON)
348     {
349         if (ival)
350             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), !!ival);
351     }
352     else if (type == GTK_TYPE_CHECK_BUTTON)
353     {
354         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), ival);
355     }
356     else if (type == GTK_TYPE_TOGGLE_TOOL_BUTTON)
357     {
358         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), ival);
359     }
360     else if (type == GTK_TYPE_TOGGLE_BUTTON)
361     {
362         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), ival);
363     }
364     else if (type == GTK_TYPE_CHECK_MENU_ITEM)
365     {
366         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), ival);
367     }
368     else if (type == GTK_TYPE_COMBO_BOX)
369     {
370         GtkTreeModel *store;
371         GtkTreeIter iter;
372         gchar *shortOpt;
373         gdouble ivalue;
374         gboolean foundit = FALSE;
375 
376         store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
377         if (gtk_tree_model_get_iter_first (store, &iter))
378         {
379             do
380             {
381                 gtk_tree_model_get(store, &iter, 2, &shortOpt, -1);
382                 if (strcasecmp(shortOpt, str) == 0)
383                 {
384                     gtk_combo_box_set_active_iter (
385                         GTK_COMBO_BOX(widget), &iter);
386                     g_free(shortOpt);
387                     foundit = TRUE;
388                     break;
389                 }
390                 g_free(shortOpt);
391             } while (gtk_tree_model_iter_next (store, &iter));
392         }
393         if (!foundit && gtk_tree_model_get_iter_first (store, &iter))
394         {
395             do
396             {
397                 gtk_tree_model_get(store, &iter, 3, &ivalue, -1);
398                 if ((gint)ivalue == ival || ivalue == dval)
399                 {
400                     gtk_combo_box_set_active_iter (
401                         GTK_COMBO_BOX(widget), &iter);
402                     foundit = TRUE;
403                     break;
404                 }
405             } while (gtk_tree_model_iter_next (store, &iter));
406         }
407         if (!foundit)
408         {
409             if (gtk_combo_box_get_has_entry(GTK_COMBO_BOX(widget)))
410             {
411                 GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(widget)));
412                 if (entry)
413                 {
414                     ghb_editable_set_text(entry, str);
415                 }
416                 else
417                 {
418                     gtk_combo_box_set_active (GTK_COMBO_BOX(widget), 0);
419                 }
420             }
421             else
422             {
423                 gtk_combo_box_set_active (GTK_COMBO_BOX(widget), 0);
424             }
425         }
426     }
427     else if (type == GTK_TYPE_SPIN_BUTTON)
428     {
429         gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), dval);
430     }
431     else if (type == GTK_TYPE_SCALE)
432     {
433         gtk_range_set_value(GTK_RANGE(widget), dval);
434     }
435     else if (type == GTK_TYPE_SCALE_BUTTON)
436     {
437         gtk_scale_button_set_value(GTK_SCALE_BUTTON(widget), dval);
438     }
439     else if (type == GTK_TYPE_TEXT_VIEW)
440     {
441         static int text_view_busy = 0;
442         GtkTextBuffer *buffer = gtk_text_view_get_buffer(
443                                                 GTK_TEXT_VIEW(widget));
444         if (!text_view_busy)
445         {
446             text_view_busy = 1;
447             gtk_text_buffer_set_text (buffer, str, -1);
448             text_view_busy = 0;
449         }
450     }
451     else if (type == GTK_TYPE_LABEL)
452     {
453         gtk_label_set_markup (GTK_LABEL(widget), str);
454     }
455     else if (type == GTK_TYPE_FILE_CHOOSER_BUTTON)
456     {
457         GtkFileChooserAction act;
458         act = gtk_file_chooser_get_action(GTK_FILE_CHOOSER(widget));
459 
460         if (str[0] == 0)
461         {
462             // Do nothing
463             ;
464         }
465         else if (act == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
466                  act == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
467         {
468             gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(widget), str);
469         }
470         else if (act == GTK_FILE_CHOOSER_ACTION_SAVE)
471         {
472             gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(widget), str);
473         }
474         else
475         {
476             if (g_file_test(str, G_FILE_TEST_IS_DIR))
477             {
478                 gtk_file_chooser_set_current_folder(
479                     GTK_FILE_CHOOSER(widget), str);
480             }
481             else if (g_file_test(str, G_FILE_TEST_EXISTS))
482             {
483                 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(widget), str);
484             }
485             else
486             {
487                 gchar * dirname;
488 
489                 dirname = g_path_get_dirname(str);
490                 gtk_file_chooser_set_current_folder(
491                     GTK_FILE_CHOOSER(widget), dirname);
492                 gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(widget));
493                 g_free(dirname);
494             }
495         }
496     }
497     else
498     {
499         g_warning("Attempt to set unknown widget type, name %s", name);
500     }
501     g_free(tmp);
502 }
503 
504 int
ghb_ui_update_from_settings(signal_user_data_t * ud,const gchar * name,const GhbValue * settings)505 ghb_ui_update_from_settings(signal_user_data_t *ud, const gchar *name, const GhbValue *settings)
506 {
507     GObject *object;
508     GhbValue * value;
509 
510     g_debug("ghb_ui_update_from_settings() %s", name);
511     if (name == NULL)
512         return 0;
513     value = ghb_dict_get_value(settings, name);
514     if (value == NULL)
515         return 0;
516     object = GHB_OBJECT(ud->builder, name);
517     if (object == NULL)
518     {
519         g_debug("Failed to find widget for key: %s\n", name);
520         return -1;
521     }
522     ghb_update_widget((GtkWidget*)object, value);
523     // Its possible the value hasn't changed. Since settings are only
524     // updated when the value changes, I'm initializing settings here as well.
525     ghb_widget_to_setting(ud->settings, (GtkWidget*)object);
526     return 0;
527 }
528 
529 int
ghb_ui_update(signal_user_data_t * ud,const gchar * name,const GhbValue * value)530 ghb_ui_update(signal_user_data_t *ud, const gchar *name, const GhbValue *value)
531 {
532     GObject *object;
533 
534     g_debug("ghb_ui_update() %s", name);
535     if (name == NULL || value == NULL)
536         return 0;
537     object = GHB_OBJECT(ud->builder, name);
538     if (object == NULL)
539     {
540         g_debug("Failed to find widget for key: %s\n", name);
541         return -1;
542     }
543     ghb_update_widget((GtkWidget*)object, value);
544     // Its possible the value hasn't changed. Since settings are only
545     // updated when the value changes, I'm initializing settings here as well.
546     ghb_widget_to_setting(ud->settings, (GtkWidget*)object);
547     return 0;
548 }
549 
550 int
ghb_ui_settings_update(signal_user_data_t * ud,GhbValue * settings,const gchar * name,const GhbValue * value)551 ghb_ui_settings_update(
552     signal_user_data_t *ud,
553     GhbValue *settings,
554     const gchar *name,
555     const GhbValue *value)
556 {
557     GObject *object;
558 
559     g_debug("ghb_ui_update() %s", name);
560     if (name == NULL || value == NULL)
561         return 0;
562     object = GHB_OBJECT(ud->builder, name);
563     if (object == NULL)
564     {
565         g_debug("Failed to find widget for key: %s\n", name);
566         return -1;
567     }
568     ghb_update_widget((GtkWidget*)object, value);
569     // Its possible the value hasn't changed. Since settings are only
570     // updated when the value changes, I'm initializing settings here as well.
571     ghb_widget_to_setting(settings, (GtkWidget*)object);
572     return 0;
573 }
574 
575