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