From 83c883338fc3151e6afa039fb2ba70ee2d2b4d87 Mon Sep 17 00:00:00 2001 From: John Stebbins Date: Thu, 28 Sep 2017 09:34:15 -0700 Subject: [PATCH] LinGui: Add "Category" dropdown to preset save dialog This replaces the "New Folder" option in the presets menu. It enforces the folder structure we have agreed to and hopefully helps the user keep things organized. Note that users are allowed to save a custom preset to the same "Category" as an official preset. When they do this, a new custom category is created with the same name and the preset is saved in that folder. --- gtk/src/callbacks.c | 12 +- gtk/src/ghb.m4 | 106 ++++++--- gtk/src/hb-backend.c | 72 ++++++ gtk/src/internal_defaults.json | 1 + gtk/src/main.c | 5 +- gtk/src/makedeps.py | 2 + gtk/src/presets.c | 409 +++++++++++++++++---------------- gtk/src/presets.h | 2 +- libhb/preset.c | 35 ++- libhb/preset.h | 12 +- test/test.c | 3 +- 11 files changed, 407 insertions(+), 252 deletions(-) diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c index c2f709f18..db31cd2e1 100644 --- a/gtk/src/callbacks.c +++ b/gtk/src/callbacks.c @@ -1175,15 +1175,17 @@ check_chapter_markers(signal_user_data_t *ud) void ghb_load_settings(signal_user_data_t * ud) { - const char *fullname; - gboolean preset_modified; - static gboolean busy = FALSE; + const char * fullname; + int type; + gboolean preset_modified; + static gboolean busy = FALSE; if (busy) return; busy = TRUE; - fullname = ghb_dict_get_string(ud->settings, "PresetFullName"); + fullname = ghb_dict_get_string(ud->settings, "PresetFullName"); + type = ghb_dict_get_int(ud->settings, "Type"); preset_modified = ghb_dict_get_bool(ud->settings, "preset_modified"); if (preset_modified) { @@ -1192,7 +1194,7 @@ ghb_load_settings(signal_user_data_t * ud) else { ghb_dict_set_bool(ud->settings, "preset_reload", TRUE); - ghb_select_preset(ud, fullname); + ghb_select_preset(ud, fullname, type); ghb_dict_set_bool(ud->settings, "preset_reload", FALSE); } diff --git a/gtk/src/ghb.m4 b/gtk/src/ghb.m4 index f93e593d1..c4c2f04fe 100644 --- a/gtk/src/ghb.m4 +++ b/gtk/src/ghb.m4 @@ -146,15 +146,6 @@ conjunction with the "Forced" option. app.preset-default - - - _New Folder - True - False - True - app.preset-folder - - _Export @@ -958,15 +949,6 @@ libx264 authors: app.preset-default - - - _New Folder - True - False - True - app.preset-folder - - _Export @@ -8378,23 +8360,91 @@ Check this if you want the queue to clean itself up by deleting completed jobs.< False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - - horizontal + True + 2 + 6 False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + Category: + + + 0 + 0 + 1 + 1 + + + + + GTK_ALIGN_CENTER + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Set the category that this preset will be shown under. + + + + 0 + 1 + 1 + 1 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + end + Category Name: + + + 1 + 0 + 1 + 1 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 40 + True + 30 + True + False + False + + + + 1 + 1 + 1 + 1 + + True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - start + end Preset Name: - False - True - 0 + 2 + 0 + 1 + 1 @@ -8408,11 +8458,13 @@ Check this if you want the queue to clean itself up by deleting completed jobs.< True False False + - True - True - 1 + 2 + 1 + 1 + 1 diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index c38835959..3f7b1fd68 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -457,6 +457,8 @@ static void video_level_opts_set(signal_user_data_t *ud, const gchar *name, void *opts, const void* data); static void container_opts_set(signal_user_data_t *ud, const gchar *name, void *opts, const void* data); +static void preset_category_opts_set(signal_user_data_t *ud, const gchar *name, + void *opts, const void* data); static void filter_opts_set(signal_user_data_t *ud, const gchar *name, void *opts, const void* data); static void deint_opts_set(signal_user_data_t *ud, const gchar *name, @@ -749,6 +751,12 @@ combo_name_map_t combo_name_map[] = container_opts_set, NULL }, + { + "PresetCategory", + NULL, + preset_category_opts_set, + NULL + }, {NULL, NULL, NULL, NULL} }; @@ -1899,6 +1907,70 @@ container_opts_set(signal_user_data_t *ud, const gchar *name, } } +static void +preset_category_opts_set(signal_user_data_t *ud, const gchar *name, + void *opts, const void* data) +{ + (void)opts; // Silence "unused variable" warning + (void)data; // Silence "unused variable" warning + GtkTreeIter iter; + GtkListStore * store; + gint ii, jj, count; + hb_value_t * presets; + GtkComboBox * combo; + char ** categories; + + presets = hb_presets_get(); + count = hb_value_array_len(presets); + + combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, name)); + store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); + gtk_list_store_clear(store); + + categories = calloc(count + 1, sizeof(char*)); + for (ii = 0, jj = 0; ii < count; ii++) + { + const char * name; + hb_value_t * folder = hb_value_array_get(presets, ii); + + if (!hb_value_get_bool(hb_dict_get(folder, "Folder"))) + { + // Only list folders + continue; + } + + name = hb_value_get_string(hb_dict_get(folder, "PresetName")); + if (name == NULL || name[0] == 0) + { + continue; + } + + if (g_strv_contains((const char**)categories, name)) + { + // Category is already in the list + continue; + } + + categories[jj++] = g_strdup(name); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + 0, name, + 1, TRUE, + 2, name, + 3, (gdouble)ii, + -1); + } + g_strfreev(categories); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + 0, "Add New Category", + 1, TRUE, + 2, "new", + 3, -1.0, + -1); +} + const hb_container_t* ghb_lookup_container_by_name(const gchar *name) { diff --git a/gtk/src/internal_defaults.json b/gtk/src/internal_defaults.json index be47bfbc4..77a5df2c3 100644 --- a/gtk/src/internal_defaults.json +++ b/gtk/src/internal_defaults.json @@ -41,6 +41,7 @@ "MetaDescription": "", "MetaLongDescription": "", "PresetFullName": "/Regular/Normal", + "PresetCategory": "new", "preset_modified": false, "preset_reload": false, "PictureDisplayWidth": 720, diff --git a/gtk/src/main.c b/gtk/src/main.c index aacb72d5c..8270a58f5 100644 --- a/gtk/src/main.c +++ b/gtk/src/main.c @@ -861,8 +861,6 @@ preset_remove_action_cb(GSimpleAction *action, GVariant *param, gpointer ud); G_MODULE_EXPORT void preset_default_action_cb(GSimpleAction *action, GVariant *param, gpointer ud); G_MODULE_EXPORT void -preset_folder_action_cb(GSimpleAction *action, GVariant *param, gpointer ud); -G_MODULE_EXPORT void preset_export_action_cb(GSimpleAction *action, GVariant *param, gpointer ud); G_MODULE_EXPORT void preset_import_action_cb(GSimpleAction *action, GVariant *param, gpointer ud); @@ -904,7 +902,6 @@ static void map_actions(GApplication * app, signal_user_data_t * ud) { "preset-save", preset_save_action_cb }, { "preset-remove", preset_remove_action_cb }, { "preset-default", preset_default_action_cb }, - { "preset-folder", preset_folder_action_cb }, { "preset-export", preset_export_action_cb }, { "preset-import", preset_import_action_cb }, { "presets-reload", presets_reload_action_cb }, @@ -940,7 +937,7 @@ ghb_idle_ui_init(signal_user_data_t *ud) if (arg_preset != NULL) { - ghb_select_preset(ud, arg_preset); + ghb_select_preset(ud, arg_preset, HB_PRESET_TYPE_ALL); } else { diff --git a/gtk/src/makedeps.py b/gtk/src/makedeps.py index 759898400..69606aa84 100644 --- a/gtk/src/makedeps.py +++ b/gtk/src/makedeps.py @@ -65,6 +65,8 @@ dep_map = ( DepEntry("VideoEncoder", "x264_box", "x264|x264_10bit", False, True), DepEntry("x264UseAdvancedOptions", "x264_box", "0", True, False), DepEntry("auto_name", "autoname_box", "1", False, False), + DepEntry("PresetCategory", "PresetCategoryName", "new", False, True), + DepEntry("PresetCategory", "PresetCategoryEntryLabel", "new", False, True), ) def main(): diff --git a/gtk/src/presets.c b/gtk/src/presets.c index 955d9f9db..87ca95860 100644 --- a/gtk/src/presets.c +++ b/gtk/src/presets.c @@ -45,13 +45,6 @@ #define MAX_NESTED_PRESET 3 -enum -{ - PRESETS_INVALID = -1, - PRESETS_BUILTIN = 0, - PRESETS_CUSTOM -}; - static GhbValue *prefsDict = NULL; static gboolean prefs_modified = FALSE; static gchar *override_user_config_dir = NULL; @@ -622,11 +615,11 @@ select_preset2(signal_user_data_t *ud, hb_preset_index_t *path) } void -ghb_select_preset(signal_user_data_t *ud, const char *name) +ghb_select_preset(signal_user_data_t *ud, const char *name, int type) { hb_preset_index_t *path; - path = hb_preset_search_index(name, 1); + path = hb_preset_search_index(name, 1, type); if (path != NULL) { select_preset2(ud, path); @@ -1110,7 +1103,7 @@ get_preset_color(gint type, gboolean is_folder) { const gchar *color; - if (type == PRESETS_CUSTOM) + if (type == HB_PRESET_TYPE_CUSTOM) { color = "DimGray"; if (is_folder) @@ -1150,9 +1143,10 @@ G_MODULE_EXPORT void preset_select_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) { - const char * preset_path = g_variant_get_string(param, NULL); + const char * preset_path = g_variant_get_string(param, NULL); + int type = preset_path[0] - '0'; - ghb_select_preset(ud, preset_path); + ghb_select_preset(ud, &preset_path[1], type); } G_MODULE_EXPORT void @@ -1160,21 +1154,24 @@ preset_reload_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) { const char * preset_path; + int type; + type = ghb_dict_get_int(ud->settings, "Type"); preset_path = ghb_dict_get_string(ud->settings, "PresetFullName"); if (preset_path != NULL) { - ghb_select_preset(ud, preset_path); + ghb_select_preset(ud, preset_path, type); } } void ghb_presets_menu_init(signal_user_data_t *ud) { - GMenu * menu = g_menu_new(); - hb_preset_index_t * path; - GhbValue * presets; - int menu_count, submenu_count, type, ii, jj; + GMenu * menu = g_menu_new(); + hb_preset_index_t * path; + GhbValue * presets; + int menu_count, submenu_count, type, ii, jj, kk; + char ** official_names; // Add official presets path = hb_preset_index_init(NULL, 0); @@ -1187,6 +1184,11 @@ ghb_presets_menu_init(signal_user_data_t *ud) } menu_count = ghb_array_len(presets); + // Menus can't contain the same name twice. Since our preset list + // allows official and custom preset categories with the same name + // I must modify one of them when duplicates exist :( + official_names = calloc(menu_count + 1, sizeof(char*)); + kk = 0; path->depth++; // Process Official Presets in first pass, then Custom Presets for (type = 0; type < 2; type++) @@ -1200,6 +1202,7 @@ ghb_presets_menu_init(signal_user_data_t *ud) gboolean is_folder; GhbValue * folder; GString * folder_str; + char * menu_item_name; path->index[path->depth-1] = ii; @@ -1213,8 +1216,15 @@ ghb_presets_menu_init(signal_user_data_t *ud) continue; } + if (type == 0) + { + // Add folder name to list of official names + official_names[kk++] = g_strdup(folder_name); + } + folder_str = g_string_new(""); - g_string_append_printf(folder_str, "/%s", folder_name); + g_string_append_printf(folder_str, "%d/%s", + folder_type, folder_name); if (is_folder) { GMenu * submenu = g_menu_new(); @@ -1252,14 +1262,25 @@ ghb_presets_menu_init(signal_user_data_t *ud) g_menu_append(submenu, name, detail_action); free(preset_path); } - g_menu_append_submenu(section, folder_name, + if (type == 1 && + g_strv_contains((const char**)official_names, folder_name)) + { + menu_item_name = g_strdup_printf("My %s", folder_name); + } + else + { + menu_item_name = g_strdup(folder_name); + } + g_menu_append_submenu(section, menu_item_name, G_MENU_MODEL(submenu)); + g_free(menu_item_name); } g_string_free(folder_str, TRUE); } g_menu_append_section(menu, type ? "Custom" : "Official", G_MENU_MODEL(section)); } + g_strfreev(official_names); GtkMenuButton * mb; @@ -1357,7 +1378,7 @@ ghb_presets_list_init(signal_user_data_t *ud, const hb_preset_index_t *path) 2, def ? 2 : 0, 3, color, 4, description, - 5, type == PRESETS_BUILTIN ? 0 : 1, + 5, type == HB_PRESET_TYPE_OFFICIAL ? 0 : 1, -1); if (is_folder) { @@ -1444,7 +1465,7 @@ presets_list_update_item( 2, def ? 2 : 0, 3, color, 4, description, - 5, type == PRESETS_BUILTIN ? 0 : 1, + 5, type == HB_PRESET_TYPE_OFFICIAL ? 0 : 1, -1); if (recurse && is_folder) { @@ -1509,7 +1530,7 @@ presets_list_append(signal_user_data_t *ud, const hb_preset_index_t *path) 2, def ? 2 : 0, 3, color, 4, description, - 5, type == PRESETS_BUILTIN ? 0 : 1, + 5, type == HB_PRESET_TYPE_OFFICIAL ? 0 : 1, -1); if (is_folder) { @@ -1617,7 +1638,7 @@ ghb_settings_to_preset(GhbValue *settings) gboolean autoscale, br, constant; ghb_dict_set_bool(preset, "Default", 0); - ghb_dict_set_int(preset, "Type", PRESETS_CUSTOM); + ghb_dict_set_int(preset, "Type", HB_PRESET_TYPE_CUSTOM); if (!ghb_dict_get_bool(preset, "PictureWidthEnable")) { ghb_dict_remove(preset, "PictureWidth"); @@ -1818,94 +1839,78 @@ ghb_presets_load(signal_user_data_t *ud) store_presets(); } } + ghb_update_ui_combo_box(ud, "PresetCategory", NULL, FALSE); } static void -settings_save(signal_user_data_t *ud, hb_preset_index_t *path, const char *name) -{ - GhbValue *dict; - gboolean replace = FALSE, def = FALSE; - - dict = hb_preset_get(path); - if (dict != NULL) - { - gboolean is_folder; - int type; - const char *s; - - def = ghb_dict_get_bool(dict, "Default"); - is_folder = ghb_dict_get_bool(dict, "Folder"); - type = ghb_dict_get_int(dict, "Type"); - s = ghb_dict_get_string(dict, "PresetName"); - if (s == NULL || strcmp(s, name) || type != PRESETS_CUSTOM || is_folder) +settings_save(signal_user_data_t *ud, const char * category, + const char *name, const char * desc) +{ + GhbValue * preset, * new_preset; + gboolean def = FALSE; + hb_preset_index_t * folder_path, * path; + char * fullname; + + folder_path = hb_preset_search_index(category, 0, HB_PRESET_TYPE_CUSTOM); + if (folder_path->depth <= 0) + { + GhbValue * new_folder; + new_folder = ghb_dict_new(); + ghb_dict_set_string(new_folder, "PresetName", category); + ghb_dict_set(new_folder, "ChildrenArray", ghb_array_new()); + ghb_dict_set_int(new_folder, "Type", HB_PRESET_TYPE_CUSTOM); + ghb_dict_set_bool(new_folder, "Folder", TRUE); + int index = hb_preset_append(folder_path, new_folder); + if (index >= 0) { - // Name changed or original preset was builtin or original - // was a folder. Don't replace it. - replace = FALSE; - path->depth--; - if (type != PRESETS_CUSTOM) - { - // Don't put new custom presets in a builtin folder - path->depth = 0; - } + folder_path->index[folder_path->depth++] = index; + presets_list_append(ud, folder_path); } else { - replace = TRUE; + ghb_log("Failed to create category (%s)...", category); + return; } + ghb_value_free(&new_folder); } - char * new_name = strdup(name); - if (!replace) - { - // We are creating a new preset. Make sure there is not - // another preset in this folder that has the same name - int ii, count, index = 1; - GhbValue *children; - children = hb_presets_get_folder_children(path); - count = ghb_array_len(children); - do - { - for (ii = 0; ii < count; ii++) - { - GhbValue *preset; - const char *s; + new_preset = ghb_settings_to_preset(ud->settings); + ghb_dict_set_string(new_preset, "PresetName", name); + ghb_dict_set_string(new_preset, "PresetDescription", desc); - preset = ghb_array_get(children, ii); - s = ghb_dict_get_string(preset, "PresetName"); - if (s != NULL && !strcmp(s, new_name)) - { - free(new_name); - new_name = g_strdup_printf("%s (%d)", name, index++); - break; - } - } - } while (ii < count); - } - dict = ghb_settings_to_preset(ud->settings); - ghb_dict_set_string(dict, "PresetName", new_name); - free(new_name); - if (replace) + fullname = g_strdup_printf("/%s/%s", category, name); + path = hb_preset_search_index(fullname, 0, HB_PRESET_TYPE_CUSTOM); + preset = hb_preset_get(path); + if (preset != NULL) { + // Replacing an existing preset + def = ghb_dict_get_bool(preset, "Default"); + // If we are replacing the default preset, re-mark it as default - ghb_dict_set_bool(dict, "Default", def); + ghb_dict_set_bool(new_preset, "Default", def); // Already exists, update its description - if (hb_preset_set(path, dict) >= 0) + if (hb_preset_set(path, new_preset) >= 0) { presets_list_update_item(ud, path, FALSE); } } else { - // Append to the folder list the source came from - int index = hb_preset_append(path, dict); + // Adding a new preset + // Append to the folder + int index = hb_preset_append(folder_path, new_preset); if (index >= 0) { - path->index[path->depth++] = index; - presets_list_append(ud, path); + folder_path->index[folder_path->depth++] = index; + presets_list_append(ud, folder_path); } + *path = *folder_path; } - ghb_value_free(&dict); + + + free(fullname); + ghb_value_free(&new_preset); + store_presets(); ghb_presets_menu_reinit(ud); @@ -1913,63 +1918,9 @@ settings_save(signal_user_data_t *ud, hb_preset_index_t *path, const char *name) // Make the new preset the selected item select_preset2(ud, path); ud->dont_clear_presets = FALSE; - return; -} -static void -folder_save(signal_user_data_t *ud, hb_preset_index_t *path, const char *name) -{ - GhbValue *dict; - - dict = hb_preset_get(path); - if (dict != NULL) - { - gboolean is_folder; - int type; - const char *s; - - is_folder = ghb_dict_get_bool(dict, "Folder"); - type = ghb_dict_get_int(dict, "Type"); - s = ghb_dict_get_string(dict, "PresetName"); - if (s == NULL || strcmp(s, name) || type != PRESETS_CUSTOM || !is_folder) - { - // Name changed or original preset was builtin or original - // was a not a folder. Don't replace it. - dict = NULL; - path->depth--; - if (type != PRESETS_CUSTOM) - { - // Don't put new custom presets in a builtin folder - path->depth = 1; - } - } - } - if (dict != NULL) - { - // Already exists, update its description - dict = hb_preset_get(path); - ghb_dict_set(dict, "PresetDescription", - ghb_value_dup(ghb_dict_get(ud->settings, "PresetDescription"))); - presets_list_update_item(ud, path, FALSE); - } - else - { - dict = ghb_dict_new(); - ghb_dict_set(dict, "PresetDescription", - ghb_value_dup(ghb_dict_get(ud->settings, "PresetDescription"))); - ghb_dict_set_string(dict, "PresetName", name); - ghb_dict_set(dict, "ChildrenArray", ghb_array_new()); - ghb_dict_set_int(dict, "Type", PRESETS_CUSTOM); - ghb_dict_set_bool(dict, "Folder", TRUE); - int index = hb_preset_append(path, dict); - if (index >= 0) - { - path->index[path->depth++] = index; - presets_list_append(ud, path); - } - ghb_value_free(&dict); - } - store_presets(); + free(folder_path); + free(path); return; } @@ -2139,65 +2090,76 @@ preset_export_action_cb(GSimpleAction *action, GVariant *param, hb_value_free(&dict); } -G_MODULE_EXPORT void -preset_folder_action_cb(GSimpleAction *action, GVariant *param, - signal_user_data_t *ud) -{ - GtkWidget *dialog; - GtkEntry *entry; - GtkTextView *desc; - GtkResponseType response; - hb_preset_index_t *path; - const gchar *name; - const gchar *description; - - path = get_selected_path(ud); - name = ghb_dict_get_string(ud->settings, "PresetName"); - description = ghb_dict_get_string(ud->settings, "PresetDescription"); - ghb_ui_update(ud, "FolderDescription", ghb_string_value(description)); - - desc = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "FolderDescription")); - dialog = GHB_WIDGET(ud->builder, "preset_new_folder_dialog"); - entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "FolderName")); - gtk_entry_set_text(entry, name); - response = gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_hide(dialog); - if (response == GTK_RESPONSE_OK) - { - // save the preset - const gchar *name = gtk_entry_get_text(entry); - GhbValue *val = ghb_widget_value(GTK_WIDGET(desc)); - ghb_dict_set(ud->settings, "PresetDescription", ghb_value_dup(val)); - folder_save(ud, path, name); - } - free(path); -} - G_MODULE_EXPORT void preset_save_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) { - const gchar *name; - const gchar *fullname; - hb_preset_index_t *path; - int width, height; - gboolean autoscale; - GtkWidget *dialog; - GtkEntry *entry; - GtkTextView *desc; - GtkResponseType response; + const char * category = NULL; + const gchar * name; + const gchar * fullname; + int type; + hb_preset_index_t * path; + int width, height; + gboolean autoscale; + GtkWidget * dialog; + GtkEntry * entry; + GtkTextView * tv; + GhbValue * dict; + GtkResponseType response; name = ghb_dict_get_string(ud->settings, "PresetName"); + type = ghb_dict_get_int(ud->settings, "Type"); fullname = ghb_dict_get_string(ud->settings, "PresetFullName"); width = ghb_dict_get_int(ud->settings, "PictureWidth"); height = ghb_dict_get_int(ud->settings, "PictureHeight"); autoscale = ghb_dict_get_bool(ud->settings, "autoscale"); + ghb_ui_update(ud, "PictureWidthEnable", ghb_boolean_value(!autoscale)); ghb_ui_update(ud, "PictureHeightEnable", ghb_boolean_value(!autoscale)); - path = hb_preset_search_index(fullname, 0); - desc = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "PresetDescription")); + path = hb_preset_search_index(fullname, 0, type); + + // Find an appropriate default category + if (path != NULL) + { + path->depth = 1; + dict = hb_preset_get(path); + if (ghb_dict_get_bool(dict, "Folder")) + { + category = ghb_dict_get_string(dict, "PresetName"); + } + free(path); + } + if (category == NULL) + { + // Find first custom folder + hb_value_t * presets; + int ii, count; + + presets = hb_presets_get(); + count = hb_value_array_len(presets); + for (ii = 0; ii < count; ii++) + { + dict = hb_value_array_get(presets, ii); + if (!ghb_dict_get_bool(dict, "Folder")) + { + continue; + } + if (ghb_dict_get_int(dict, "Type") == 1) + { + category = ghb_dict_get_string(dict, "PresetName"); + break; + } + } + } + if (category == NULL) + { + // Force creation of new category + category = "new"; + } + ghb_ui_update(ud, "PresetCategory", ghb_string_value(category)); + tv = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "PresetDescription")); if (!width) { width = ghb_dict_get_int(ud->settings, "scale_width"); @@ -2216,18 +2178,65 @@ preset_save_action_cb(GSimpleAction *action, GVariant *param, gtk_widget_hide(dialog); if (response == GTK_RESPONSE_OK) { + GtkTextBuffer * buffer; + GtkTextIter start, end; + char * desc; + // save the preset name = gtk_entry_get_text(entry); - ghb_widget_to_setting(ud->settings, GTK_WIDGET(desc)); - settings_save(ud, path, name); + category = ghb_dict_get_string(ud->settings, "PresetCategory"); + if (!strcmp(category, "new")) + { + entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetCategoryEntry")); + category = gtk_entry_get_text(entry); + } + if (category == NULL || category[0] == 0) + { + ghb_log("Invalid empty category."); + return; + } + buffer = gtk_text_view_get_buffer(tv); + gtk_text_buffer_get_bounds(buffer, &start, &end); + desc = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + settings_save(ud, category, name, desc); + free(desc); } - free(path); +} + +static void +preset_save_set_ok_sensitive(signal_user_data_t *ud) +{ + GtkEntry * entry; + GtkWidget * ok_button; + const char * name; + const char * category; + const char * category_name; + gboolean sensitive; + + ok_button = GHB_WIDGET(ud->builder, "preset_ok"); + + category = ghb_dict_get_string(ud->settings, "PresetCategory"); + entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetName")); + name = gtk_entry_get_text(entry); + entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetCategoryName")); + category_name = gtk_entry_get_text(entry); + + sensitive = name[0] && (strcmp(category, "new") || category_name[0]); + gtk_widget_set_sensitive(ok_button, sensitive); } G_MODULE_EXPORT void -preset_type_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +preset_category_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); + preset_save_set_ok_sensitive(ud); + ghb_check_dependency(ud, widget, NULL); +} + +G_MODULE_EXPORT void +preset_name_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +{ + preset_save_set_ok_sensitive(ud); } G_MODULE_EXPORT void @@ -2247,16 +2256,17 @@ G_MODULE_EXPORT void preset_remove_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) { - + int type; const char * fullname; hb_preset_index_t * path; - fullname = ghb_dict_get_string(ud->settings, "PresetFullName"); + type = ghb_dict_get_int(ud->settings, "Type"); + fullname = ghb_dict_get_string(ud->settings, "PresetFullName"); if (fullname == NULL) { return; } - path = hb_preset_search_index(fullname, 0); + path = hb_preset_search_index(fullname, 0, type); if (path == NULL) { return; @@ -2321,6 +2331,7 @@ preset_remove_action_cb(GSimpleAction *action, GVariant *param, } } free(path); + ghb_update_ui_combo_box(ud, "PresetCategory", NULL, FALSE); } // controls where valid drop locations are @@ -2380,7 +2391,7 @@ presets_drag_motion_cb( return TRUE; } // Don't allow repositioning of builtin presets - if (src_ptype != PRESETS_CUSTOM) + if (src_ptype != HB_PRESET_TYPE_CUSTOM) { gdk_drag_status(ctx, 0, time); return TRUE; @@ -2407,12 +2418,12 @@ presets_drag_motion_cb( } else { - dst_ptype = PRESETS_CUSTOM; + dst_ptype = HB_PRESET_TYPE_CUSTOM; dst_folder = FALSE; } // Don't allow mixing custom presets in the builtins - if (dst_ptype != PRESETS_CUSTOM) + if (dst_ptype != HB_PRESET_TYPE_CUSTOM) { gdk_drag_status(ctx, 0, time); return TRUE; @@ -2493,7 +2504,7 @@ presets_drag_cb( src_folder = preset_is_folder(src_path); // Don't allow repositioning of builtin presets - if (src_ptype != PRESETS_CUSTOM) + if (src_ptype != HB_PRESET_TYPE_CUSTOM) { free(src_path); return; diff --git a/gtk/src/presets.h b/gtk/src/presets.h index b71137375..cd14d48ce 100644 --- a/gtk/src/presets.h +++ b/gtk/src/presets.h @@ -42,7 +42,7 @@ gchar* ghb_get_user_config_dir(gchar *subdir); void ghb_override_user_config_dir(char *dir); void ghb_settings_to_ui(signal_user_data_t *ud, GhbValue *dict); void ghb_clear_presets_selection(signal_user_data_t *ud); -void ghb_select_preset(signal_user_data_t *ud, const char *name); +void ghb_select_preset(signal_user_data_t *ud, const char *name, int type); void ghb_select_default_preset(signal_user_data_t *ud); void ghb_presets_list_init(signal_user_data_t *ud, const hb_preset_index_t *path); diff --git a/libhb/preset.c b/libhb/preset.c index af94d8539..19a013211 100644 --- a/libhb/preset.c +++ b/libhb/preset.c @@ -73,17 +73,25 @@ typedef struct { preset_do_context_t do_ctx; const char *name; + int type; int recurse; int last_match_idx; } preset_search_context_t; typedef int (*preset_do_f)(hb_value_t *preset, preset_do_context_t *ctx); -static int preset_cmp_idx(hb_value_t *preset, int idx, const char *name) +static int preset_cmp_idx(hb_value_t *preset, int idx, + const char *name, int type) { const char *next, *preset_name; int ii, len; + if (type != HB_PRESET_TYPE_ALL && + type != hb_value_get_int(hb_dict_get(preset, "Type"))) + { + return PRESET_DO_NEXT; + } + // Strip leading '/' if (name[0] == '/') name++; @@ -134,10 +142,10 @@ static int do_preset_search(hb_value_t *preset, preset_do_context_t *do_ctx) idx -= ctx->last_match_idx; } - result = preset_cmp_idx(preset, idx, ctx->name); + result = preset_cmp_idx(preset, idx, ctx->name, ctx->type); if (ctx->recurse && result == PRESET_DO_SKIP) { - result = preset_cmp_idx(preset, 0, ctx->name); + result = preset_cmp_idx(preset, 0, ctx->name, ctx->type); ctx->last_match_idx = idx; } if (result == PRESET_DO_PARTIAL) @@ -168,7 +176,7 @@ static int do_preset_clean(hb_value_t *preset, preset_do_context_t *do_ctx) static int do_delete_builtin(hb_value_t *preset, preset_do_context_t *ctx) { - if (hb_value_get_int(hb_dict_get(preset, "Type")) == 0) + if (hb_value_get_int(hb_dict_get(preset, "Type")) == HB_PRESET_TYPE_OFFICIAL) return PRESET_DO_DELETE; return PRESET_DO_NEXT; } @@ -259,7 +267,7 @@ static int presets_do(preset_do_f do_func, hb_value_t *preset, hb_preset_index_t* hb_preset_index_init(const int *index, int depth) { hb_preset_index_t *path; - path = malloc(sizeof(hb_preset_index_t)); + path = calloc(1, sizeof(hb_preset_index_t)); path->depth = depth; if (index != NULL) memcpy(path->index, index, depth * sizeof(int)); @@ -3209,13 +3217,15 @@ char * hb_presets_builtin_get_json(void) // I assume that the actual preset name does not include any '/' // // A reference to the preset is returned -static hb_preset_index_t * preset_lookup_path(const char *name, int recurse) +static hb_preset_index_t * preset_lookup_path(const char *name, + int recurse, int type) { preset_search_context_t ctx; int result; ctx.do_ctx.path.depth = 1; ctx.name = name; + ctx.type = type; ctx.recurse = recurse; ctx.last_match_idx = -1; result = presets_do(do_preset_search, hb_presets, @@ -3236,24 +3246,25 @@ static hb_preset_index_t * preset_lookup_path(const char *name, int recurse) // I assume that the actual preset name does not include any '/' // // A copy of the preset is returned -hb_preset_index_t * hb_preset_search_index(const char *name, int recurse) +hb_preset_index_t * hb_preset_search_index(const char *name, + int recurse, int type) { - return preset_lookup_path(name, recurse); + return preset_lookup_path(name, recurse, type); } -hb_value_t * hb_preset_search(const char *name, int recurse) +hb_value_t * hb_preset_search(const char *name, int recurse, int type) { - hb_preset_index_t *path = preset_lookup_path(name, recurse); + hb_preset_index_t *path = preset_lookup_path(name, recurse, type); hb_value_t *preset = hb_preset_get(path); free(path); return preset; } -char * hb_preset_search_json(const char *name, int recurse) +char * hb_preset_search_json(const char *name, int recurse, int type) { hb_value_t * preset; char *json; - preset = hb_preset_search(name, recurse); + preset = hb_preset_search(name, recurse, type); if (preset == NULL) return NULL; json = hb_value_get_json(preset); diff --git a/libhb/preset.h b/libhb/preset.h index e797edeb6..61ffaf9b6 100644 --- a/libhb/preset.h +++ b/libhb/preset.h @@ -14,6 +14,10 @@ #define HB_MAX_PRESET_FOLDER_DEPTH 8 +#define HB_PRESET_TYPE_OFFICIAL 0 +#define HB_PRESET_TYPE_CUSTOM 1 +#define HB_PRESET_TYPE_ALL 2 + typedef struct hb_preset_index_s hb_preset_index_t; // A preset index is a list of indexes that specifies a path to a @@ -160,9 +164,11 @@ void hb_sanitize_audio_settings(const hb_title_t * title, // in the name will be performed. // // I assume that the actual preset name does not include any '/' -hb_preset_index_t * hb_preset_search_index(const char *name, int recurse); -hb_value_t * hb_preset_search(const char *name, int recurse); -char * hb_preset_search_json(const char *name, int recurse); +hb_preset_index_t * hb_preset_search_index(const char *name, + int recurse, int type); +hb_value_t * hb_preset_search(const char *name, int recurse, int type); +char * hb_preset_search_json(const char *name, + int recurs, int typee); hb_value_t * hb_presets_get_folder_children(const hb_preset_index_t *path); hb_value_t * hb_preset_get(const hb_preset_index_t *path); diff --git a/test/test.c b/test/test.c index e2336dab0..1e3238989 100644 --- a/test/test.c +++ b/test/test.c @@ -3260,7 +3260,8 @@ static hb_dict_t * PreparePreset(const char *preset_name) if (preset_name != NULL) { - preset = hb_preset_search(preset_name, 1 /*recurse*/); + preset = hb_preset_search(preset_name, 1 /*recurse*/, + HB_PRESET_TYPE_ALL); if (preset == NULL) { fprintf(stderr, "Invalid preset %s\n" -- 2.40.0