+ *len = ii;
+ return indices;
+}
+
+static gint
+preset_tree_depth(GValue *dict)
+{
+ gboolean folder;
+
+ folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder"));
+ if (folder)
+ {
+ gint depth = 0;
+ gint count, ii;
+ GValue *presets;
+
+ presets = ghb_dict_lookup(dict, "ChildrenArray");
+ count = ghb_array_len(presets);
+ for (ii = 0; ii < count; ii++)
+ {
+ gint tmp;
+
+ dict = ghb_array_get_nth(presets, ii);
+ tmp = preset_tree_depth(dict);
+ depth = MAX(depth, tmp);
+ }
+ return depth + 1;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+static gboolean
+preset_is_default(GValue *dict)
+{
+ const GValue *val;
+
+ val = preset_dict_get_value(dict, "Default");
+ return ghb_value_boolean(val);
+}
+
+static void
+presets_clear_default(GValue *presets)
+{
+ gint count, ii;
+
+ count = ghb_array_len(presets);
+ for (ii = 0; ii < count; ii++)
+ {
+ GValue *dict;
+ gboolean folder;
+
+ dict = ghb_array_get_nth(presets, ii);
+ folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder"));
+ if (folder)
+ {
+ GValue *nested;
+
+ nested = ghb_dict_lookup(dict, "ChildrenArray");
+ presets_clear_default(nested);
+ }
+ else
+ {
+ if (preset_is_default(dict))
+ {
+ ghb_dict_insert(dict, g_strdup("Default"),
+ ghb_boolean_value_new(FALSE));
+ }
+ }
+ }
+}
+
+static void
+presets_customize(GValue *presets)
+{
+ gint count, ii;
+
+ count = ghb_array_len(presets);
+ for (ii = 0; ii < count; ii++)
+ {
+ GValue *dict;
+ gboolean folder;
+ gint ptype;
+
+ dict = ghb_array_get_nth(presets, ii);
+
+ ptype = ghb_value_int(preset_dict_get_value(dict, "Type"));
+ if (ptype != PRESETS_CUSTOM)
+ {
+ ghb_dict_insert(dict, g_strdup("Type"),
+ ghb_int64_value_new(PRESETS_CUSTOM));
+ }
+ folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder"));
+ if (folder)
+ {
+ GValue *nested;
+
+ nested = ghb_dict_lookup(dict, "ChildrenArray");
+ presets_customize(nested);
+ }
+ }
+}
+
+static gint*
+presets_find_default2(GValue *presets, gint *len)
+{
+ gint count, ii;
+ gint *indices;
+
+ count = ghb_array_len(presets);
+ for (ii = 0; ii < count; ii++)
+ {
+ GValue *dict;
+ gboolean folder;
+
+ dict = ghb_array_get_nth(presets, ii);
+ folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder"));
+ if (folder)
+ {
+ GValue *nested;
+ gint pos = *len;
+
+ nested = ghb_dict_lookup(dict, "ChildrenArray");
+ (*len)++;
+ indices = presets_find_default2(nested, len);
+ if (indices)
+ {
+ indices[pos] = ii;
+ return indices;
+ }
+ else
+ *len = pos;
+ }
+ else
+ {
+ if (preset_is_default(dict))
+ {
+ indices = g_malloc(MAX_NESTED_PRESET * sizeof(gint));
+ indices[*len] = ii;
+ (*len)++;
+ return indices;
+ }
+ }
+ }
+ return NULL;
+}
+
+static gint*
+presets_find_default(GValue *presets, gint *len)
+{
+ *len = 0;
+ return presets_find_default2(presets, len);
+}
+
+gint*
+ghb_preset_indices_from_path(
+ GValue *presets,
+ const GValue *path,
+ gint *len)
+{
+ GValue *nested;
+ GValue *val;
+ gint count, ii;
+ gint *indices = NULL;
+ const gchar *name;
+ GValue *dict;
+ gboolean folder;
+
+ g_debug("ghb_preset_indices_from_path () ");
+ nested = presets;
+ count = ghb_array_len(path);
+ if (count)
+ indices = g_malloc(MAX_NESTED_PRESET * sizeof(gint));
+ *len = 0;
+ for (ii = 0; ii < count; ii++)
+ {
+ val = ghb_array_get_nth(path, ii);
+ name = g_value_get_string(val);
+ indices[ii] = presets_find_element(nested, name);
+ if (indices[ii] == -1)
+ {
+ g_free(indices);
+ return NULL;
+ }
+ if (ii < count-1)
+ {
+ dict = ghb_array_get_nth(nested, indices[ii]);
+ folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder"));
+ if (!folder)
+ {
+ g_free(indices);
+ return NULL;
+ }
+ nested = ghb_dict_lookup(dict, "ChildrenArray");
+ }
+ }
+ *len = ii;
+ return indices;
+}
+
+static gint
+ghb_presets_get_type(
+ GValue *presets,
+ gint *indices,
+ gint len)
+{
+ GValue *dict;
+ gint type = 0;
+
+ dict = presets_get_dict(presets, indices, len);
+ if (dict)
+ {
+ type = ghb_preset_type(dict);
+ }
+ else
+ {
+ g_warning("ghb_presets_get_type (): internal preset lookup error");
+ }
+ return type;
+}
+
+static gboolean
+ghb_presets_get_folder(
+ GValue *presets,
+ gint *indices,
+ gint len)
+{
+ GValue *dict;
+ gboolean folder = FALSE;
+
+ dict = presets_get_dict(presets, indices, len);
+ if (dict)
+ {
+ folder = ghb_preset_folder(dict);
+ }
+ else
+ {
+ g_warning("ghb_presets_get_folder (): internal preset lookup error");
+ }
+ return folder;
+}
+
+void
+presets_set_default(gint *indices, gint len)
+{
+ GValue *dict;
+
+ g_debug("presets_set_default ()");
+ presets_clear_default(presetsPlist);
+ dict = presets_get_dict(presetsPlist, indices, len);
+ if (dict)
+ {
+ ghb_dict_insert(dict, g_strdup("Default"), ghb_boolean_value_new(TRUE));
+ }
+ store_presets();
+}
+
+static void
+presets_set_folder_open(gboolean open, gint *indices, gint len)
+{
+ GValue *dict;
+
+ g_debug("presets_set_folder_open ()");
+ dict = presets_get_dict(presetsPlist, indices, len);
+ if (dict)
+ {
+ ghb_dict_insert(dict, g_strdup("FolderOpen"),
+ ghb_boolean_value_new(open));
+ }
+}
+
+// Used for sorting dictionaries.
+gint
+key_cmp(gconstpointer a, gconstpointer b)
+{
+ gchar *stra = (gchar*)a;
+ gchar *strb = (gchar*)b;
+
+ return strcmp(stra, strb);
+}
+
+static const GValue*
+preset_dict_get_value(GValue *dict, const gchar *key)
+{
+ const GValue *gval = NULL;
+
+ if (dict)
+ {
+ gval = ghb_dict_lookup(dict, key);
+ }
+ if (internalPlist == NULL) return NULL;
+ if (gval == NULL)
+ {
+ dict = plist_get_dict(internalPlist, "Presets");
+ if (dict == NULL) return NULL;
+ gval = ghb_dict_lookup(dict, key);
+ }
+ return gval;
+}
+
+const gchar*
+ghb_presets_get_description(GValue *pdict)
+{
+ const gchar *desc;
+
+ if (pdict == NULL) return NULL;
+ desc = g_value_get_string(
+ preset_dict_get_value(pdict, "PresetDescription"));
+ if (desc[0] == 0) return NULL;
+ return desc;
+}
+
+
+static void init_settings_from_dict(
+ GValue *dest, GValue *internal, GValue *dict);
+
+static void
+init_settings_from_array(
+ GValue *dest,
+ GValue *internal,
+ GValue *array)
+{
+ GValue *gval, *val;
+ gint count, ii;
+
+ count = ghb_array_len(array);
+ // The first element of the internal version is always the
+ // template for the allowed values
+ gval = ghb_array_get_nth(internal, 0);
+ for (ii = 0; ii < count; ii++)
+ {
+ val = NULL;
+ val = ghb_array_get_nth(array, ii);
+ if (val == NULL)
+ val = gval;
+ if (G_VALUE_TYPE(gval) == ghb_dict_get_type())
+ {
+ GValue *new_dict;
+ new_dict = ghb_dict_value_new();
+ ghb_array_append(dest, new_dict);
+ if (G_VALUE_TYPE(val) == ghb_dict_get_type())
+ init_settings_from_dict(new_dict, gval, val);
+ else
+ init_settings_from_dict(new_dict, gval, gval);
+ }
+ else if (G_VALUE_TYPE(gval) == ghb_array_get_type())
+ {
+ GValue *new_array;
+ new_array = ghb_array_value_new(8);
+ ghb_array_append(dest, new_array);
+ if (G_VALUE_TYPE(val) == ghb_array_get_type())
+ init_settings_from_array(new_array, gval, val);
+ else
+ init_settings_from_array(new_array, gval, gval);
+ }
+ else
+ {
+ ghb_array_append(dest, val);
+ }
+ }
+}
+
+static void
+init_settings_from_dict(
+ GValue *dest,
+ GValue *internal,
+ GValue *dict)
+{
+ GHashTableIter iter;
+ gchar *key;
+ GValue *gval, *val;
+
+ ghb_dict_iter_init(&iter, internal);
+ // middle (void*) cast prevents gcc warning "defreferencing type-punned
+ // pointer will break strict-aliasing rules"
+ while (g_hash_table_iter_next(
+ &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
+ {
+ val = NULL;
+ if (dict)
+ val = ghb_dict_lookup(dict, key);
+ if (val == NULL)
+ val = gval;
+ if (G_VALUE_TYPE(gval) == ghb_dict_get_type())
+ {
+ GValue *new_dict;
+ new_dict = ghb_dict_value_new();
+ ghb_settings_take_value(dest, key, new_dict);
+ if (G_VALUE_TYPE(val) == ghb_dict_get_type())
+ init_settings_from_dict(new_dict, gval, val);
+ else
+ init_settings_from_dict(new_dict, gval, gval);
+ }
+ else if (G_VALUE_TYPE(gval) == ghb_array_get_type())
+ {
+ GValue *new_array;
+ new_array = ghb_array_value_new(8);
+ ghb_settings_take_value(dest, key, new_array);
+ if (G_VALUE_TYPE(val) == ghb_array_get_type())
+ init_settings_from_array(new_array, gval, val);
+ else
+ init_settings_from_array(new_array, gval, gval);
+
+ }
+ else
+ {
+ ghb_settings_set_value(dest, key, val);
+ }
+ }
+}
+
+void
+init_ui_from_dict(
+ signal_user_data_t *ud,
+ GValue *internal,
+ GValue *dict)
+{
+ GHashTableIter iter;
+ gchar *key;
+ GValue *gval, *val;
+
+ ghb_dict_iter_init(&iter, internal);
+ // middle (void*) cast prevents gcc warning "defreferencing type-punned
+ // pointer will break strict-aliasing rules"
+ while (g_hash_table_iter_next(
+ &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
+ {
+ val = NULL;
+ if (dict)
+ val = ghb_dict_lookup(dict, key);
+ if (val == NULL)
+ val = gval;
+ ghb_ui_update(ud, key, val);
+ }
+}
+
+static void
+preset_to_ui(signal_user_data_t *ud, GValue *dict)
+{
+ g_debug("preset_to_ui()\n");
+ // Initialize the ui from presets file.
+ GValue *internal, *hidden;
+
+ // Get key list from internal default presets. This way we do not
+ // load any unknown keys.
+ if (internalPlist == NULL) return;
+ internal = plist_get_dict(internalPlist, "Presets");
+ hidden = plist_get_dict(internalPlist, "XlatPresets");
+ // Setting a ui widget will cause the corresponding setting
+ // to be set, but it also triggers a callback that can
+ // have the side effect of using other settings values
+ // that have not yet been set. So set *all* settings first
+ // then update the ui.
+ init_settings_from_dict(ud->settings, internal, dict);
+ init_settings_from_dict(ud->settings, hidden, dict);
+ init_ui_from_dict(ud, internal, dict);
+ init_ui_from_dict(ud, hidden, dict);
+}
+
+void
+ghb_settings_to_ui(signal_user_data_t *ud, GValue *dict)
+{
+ init_ui_from_dict(ud, dict, dict);
+}
+
+static GValue *current_preset = NULL;
+
+gboolean
+ghb_preset_is_custom()
+{
+ const GValue *val;
+
+ if (current_preset == NULL) return FALSE;
+ val = preset_dict_get_value(current_preset, "Type");
+ return (ghb_value_int(val) == 1);
+}
+
+void
+ghb_set_preset_from_indices(signal_user_data_t *ud, gint *indices, gint len)
+{
+ GValue *dict = NULL;
+ gint fallback[2] = {0, -1};
+
+ if (indices)
+ dict = presets_get_dict(presetsPlist, indices, len);
+ if (dict == NULL)
+ {
+ indices = fallback;
+ len = 1;
+ dict = presets_get_dict(presetsPlist, indices, len);
+ }
+ if (dict == NULL)
+ {
+ preset_to_ui(ud, NULL);
+ current_preset = NULL;
+ }
+ else
+ {
+ GValue *path;
+ gboolean folder;
+
+ current_preset = dict;
+ folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder"));
+ if (folder)
+ preset_to_ui(ud, NULL);
+ else
+ preset_to_ui(ud, dict);
+ path = preset_path_from_indices(presetsPlist, indices, len);
+ ghb_settings_set_value(ud->settings, "preset", path);
+ ghb_value_free(path);
+ }
+}
+
+static const GValue*
+curr_preset_get_value(const gchar *key)
+{
+ if (current_preset == NULL) return NULL;
+ return preset_dict_get_value(current_preset, key);
+}
+
+void
+ghb_update_from_preset(
+ signal_user_data_t *ud,
+ const gchar *key)
+{
+ const GValue *gval;
+
+ g_debug("ghb_update_from_preset() %s", key);
+ gval = curr_preset_get_value(key);
+ if (gval != NULL)
+ {
+ ghb_ui_update(ud, key, gval);
+ }
+}
+
+static void
+ghb_select_preset2(
+ GtkBuilder *builder,
+ gint *indices,
+ gint len)
+{
+ GtkTreeView *treeview;
+ GtkTreeSelection *selection;
+ GtkTreeModel *store;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_debug("ghb_select_preset2()");
+ treeview = GTK_TREE_VIEW(GHB_WIDGET(builder, "presets_list"));
+ selection = gtk_tree_view_get_selection (treeview);
+ store = gtk_tree_view_get_model (treeview);
+ path = ghb_tree_path_new_from_indices(indices, len);
+ if (path)
+ {
+ if (gtk_tree_model_get_iter(store, &iter, path))
+ {
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+ else
+ {
+ if (gtk_tree_model_get_iter_first(store, &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+ gtk_tree_path_free(path);
+ }
+}
+
+void
+ghb_select_preset(GtkBuilder *builder, const GValue *path)
+{
+ gint *indices, len;
+
+ g_debug("ghb_select_preset()");
+ indices = ghb_preset_indices_from_path(presetsPlist, path, &len);
+ if (indices)
+ {
+ ghb_select_preset2(builder, indices, len);
+ g_free(indices);
+ }
+}
+
+void
+ghb_select_default_preset(GtkBuilder *builder)
+{
+ gint *indices, len;
+
+ g_debug("ghb_select_default_preset()");
+ indices = presets_find_default(presetsPlist, &len);
+ if (indices)
+ {
+ ghb_select_preset2(builder, indices, len);
+ g_free(indices);
+ }
+}
+
+gchar*
+ghb_get_user_config_dir(gchar *subdir)
+{
+ const gchar *dir;
+ gchar *config;
+
+ dir = g_get_user_config_dir();
+ if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
+ {
+ dir = g_get_home_dir();
+ config = g_strdup_printf ("%s/.ghb", dir);
+ if (!g_file_test(config, G_FILE_TEST_IS_DIR))
+ g_mkdir (config, 0755);
+ }
+ else
+ {
+ config = g_strdup_printf ("%s/ghb", dir);
+ if (!g_file_test(config, G_FILE_TEST_IS_DIR))
+ g_mkdir (config, 0755);
+ }
+ if (subdir)
+ {
+ gchar **split;
+ gint ii;
+
+ split = g_strsplit(subdir, G_DIR_SEPARATOR_S, -1);
+ for (ii = 0; split[ii] != NULL; ii++)
+ {
+ gchar *tmp;
+
+ tmp = g_strdup_printf ("%s/%s", config, split[ii]);
+ g_free(config);
+ config = tmp;
+ if (!g_file_test(config, G_FILE_TEST_IS_DIR))
+ g_mkdir (config, 0755);
+ }
+ }
+ return config;
+}
+
+static void
+store_plist(GValue *plist, const gchar *name)
+{
+ gchar *config, *path;
+ FILE *file;
+
+ config = ghb_get_user_config_dir(NULL);
+ path = g_strdup_printf ("%s/%s", config, name);
+ file = g_fopen(path, "w");
+ g_free(config);
+ g_free(path);
+ ghb_plist_write(file, plist);
+ fclose(file);
+}
+
+static GValue*
+load_plist(const gchar *name)
+{
+ gchar *config, *path;
+ GValue *plist = NULL;
+
+ config = ghb_get_user_config_dir(NULL);
+ path = g_strdup_printf ("%s/%s", config, name);
+ if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
+ {
+ plist = ghb_plist_parse_file(path);
+ }
+ g_free(config);
+ g_free(path);
+ return plist;
+}
+
+gboolean
+ghb_lock_file(const gchar *name)
+{
+#if !defined(_WIN32)
+ gchar *config, *path;
+ int fd, lock = 0;
+
+ config = ghb_get_user_config_dir(NULL);
+ path = g_strdup_printf ("%s/%s", config, name);
+ fd = open(path, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
+ if (fd >= 0)
+ lock = lockf(fd, F_TLOCK, 0);
+ if (lock)
+ close(fd);
+ g_free(config);
+ g_free(path);
+ return !lock;
+#else
+ return 1;
+#endif
+}
+
+static void
+remove_plist(const gchar *name)
+{
+ gchar *config, *path;
+
+ config = ghb_get_user_config_dir(NULL);
+ path = g_strdup_printf ("%s/%s", config, name);
+ if (g_file_test(path, G_FILE_TEST_IS_REGULAR))
+ {
+ g_unlink(path);
+ }
+ g_free(path);
+ g_free(config);
+}
+
+static gboolean prefs_initializing = FALSE;
+
+void
+ghb_prefs_to_ui(signal_user_data_t *ud)
+{
+ const GValue *gval;
+ gchar *key;
+ gchar *str;
+ GValue *internal, *dict;
+ GHashTableIter iter;
+
+
+ g_debug("ghb_prefs_to_ui");
+ prefs_initializing = TRUE;
+
+ // Setting a ui widget will cause the corresponding setting
+ // to be set, but it also triggers a callback that can
+ // have the side effect of using other settings values
+ // that have not yet been set. So set *all* settings first
+ // then update the ui.
+ internal = plist_get_dict(internalPlist, "Initialization");
+ ghb_dict_iter_init(&iter, internal);
+ // middle (void*) cast prevents gcc warning "defreferencing type-punned
+ // pointer will break strict-aliasing rules"
+ while (g_hash_table_iter_next(
+ &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
+ {
+ ghb_ui_update(ud, key, gval);
+ }
+
+ dict = plist_get_dict(prefsPlist, "Preferences");
+ internal = plist_get_dict(internalPlist, "Preferences");
+ ghb_dict_iter_init(&iter, internal);
+ // middle (void*) cast prevents gcc warning "defreferencing type-punned
+ // pointer will break strict-aliasing rules"
+ while (g_hash_table_iter_next(
+ &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
+ {
+ const GValue *value = NULL;
+ if (dict)
+ value = ghb_dict_lookup(dict, key);
+ if (value == NULL)
+ value = gval;
+ ghb_settings_set_value(ud->settings, key, value);
+ }
+ internal = plist_get_dict(internalPlist, "Preferences");
+ ghb_dict_iter_init(&iter, internal);
+ // middle (void*) cast prevents gcc warning "defreferencing type-punned
+ // pointer will break strict-aliasing rules"
+ while (g_hash_table_iter_next(
+ &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval))
+ {
+ const GValue *value = NULL;
+ if (dict)
+ value = ghb_dict_lookup(dict, key);
+ if (value == NULL)
+ value = gval;
+ ghb_ui_update(ud, key, value);
+ }
+ const GValue *val;
+ val = ghb_settings_get_value(ud->settings, "show_presets");
+ ghb_ui_update(ud, "show_presets", val);
+ if (ghb_settings_get_boolean(ud->settings, "hbfd_feature"))
+ {
+ GtkAction *action;
+ val = ghb_settings_get_value(ud->settings, "hbfd");
+ ghb_ui_update(ud, "hbfd", val);
+ action = GHB_ACTION (ud->builder, "hbfd");
+ gtk_action_set_visible(action, TRUE);
+ }
+ else
+ {
+ ghb_ui_update(ud, "hbfd", ghb_int64_value(0));
+ }
+ gval = ghb_settings_get_value(ud->settings, "default_source");
+ ghb_settings_set_value (ud->settings, "source", gval);
+
+ str = ghb_settings_get_string(ud->settings, "destination_dir");
+ ghb_ui_update(ud, "dest_dir", ghb_string_value(str));
+
+ gchar *file = g_strdup_printf ("new_video.mp4");
+ ghb_ui_update(ud, "dest_file", ghb_string_value(file));
+ g_free(str);
+ g_free(file);
+
+ prefs_initializing = FALSE;