]> granicus.if.org Git - handbrake/commitdiff
LinGui: Add "Category" dropdown to preset save dialog
authorJohn Stebbins <jstebbins.hb@gmail.com>
Thu, 28 Sep 2017 16:34:15 +0000 (09:34 -0700)
committerJohn Stebbins <jstebbins.hb@gmail.com>
Mon, 6 Nov 2017 16:19:49 +0000 (08:19 -0800)
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
gtk/src/ghb.m4
gtk/src/hb-backend.c
gtk/src/internal_defaults.json
gtk/src/main.c
gtk/src/makedeps.py
gtk/src/presets.c
gtk/src/presets.h
libhb/preset.c
libhb/preset.h
test/test.c

index c2f709f1817e3f890a28bf192f82f29561ae6111..db31cd2e159e78176e8d12eb5334961026edf732 100644 (file)
@@ -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);
     }
 
index f93e593d15b58a293bbc1142eb0855b0c3070088..c4c2f04fefbfeb5a891d6c37a29ba3bfe6859852 100644 (file)
@@ -146,15 +146,6 @@ conjunction with the "Forced" option.</property>
                         <property name="action-name">app.preset-default</property>
                       </object>
                     </child>
-                    <child>
-                      <object class="GtkMenuItem" id="presets_window_new_folder">
-                        <property name="label" translatable="yes">_New Folder</property>
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="use_underline">True</property>
-                        <property name="action-name">app.preset-folder</property>
-                      </object>
-                    </child>
                     <child>
                       <object class="GtkMenuItem" id="presets_window_export">
                         <property name="label" translatable="yes">_Export</property>
@@ -958,15 +949,6 @@ libx264 authors:
                         <property name="action-name">app.preset-default</property>
                       </object>
                     </child>
-                    <child>
-                      <object class="GtkMenuItem" id="presets_new_folder">
-                        <property name="label" translatable="yes">_New Folder</property>
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="use_underline">True</property>
-                        <property name="action-name">app.preset-folder</property>
-                      </object>
-                    </child>
                     <child>
                       <object class="GtkMenuItem" id="presets_export">
                         <property name="label" translatable="yes">_Export</property>
@@ -8378,23 +8360,91 @@ Check this if you want the queue to clean itself up by deleting completed jobs.<
             <property name="can_focus">False</property>
             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
             <child>
-              <object class="GtkBox" id="hbox41">
-                <property name="orientation">horizontal</property>
+              <object class="GtkGrid" id="preset_save_name_table">
                 <property name="visible">True</property>
+                <property name="row-spacing">2</property>
+                <property name="column-spacing">6</property>
                 <property name="can_focus">False</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <child>
+                  <object class="GtkLabel" id="preset_save_category_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="halign">end</property>
+                    <property name="label" translatable="yes">Category:</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">0</property>
+                    <property name="left_attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBox" id="PresetCategory">
+                    <property name="valign">GTK_ALIGN_CENTER</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="tooltip_text" translatable="yes">Set the category that this preset will be shown under.</property>
+                    <signal name="changed" handler="preset_category_changed_cb" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="top_attach">0</property>
+                    <property name="left_attach">1</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="PresetCategoryEntryLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="halign">end</property>
+                    <property name="label" translatable="yes">Category Name:</property>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="left_attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="PresetCategoryName">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="max_length">40</property>
+                    <property name="activates_default">True</property>
+                    <property name="width-chars">30</property>
+                    <property name="truncate_multiline">True</property>
+                    <property name="primary_icon_activatable">False</property>
+                    <property name="secondary_icon_activatable">False</property>
+                    <signal name="changed" handler="preset_category_changed_cb" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="left_attach">1</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
                 <child>
                   <object class="GtkLabel" id="label64">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                    <property name="halign">start</property>
+                    <property name="halign">end</property>
                     <property name="label" translatable="yes">Preset Name:</property>
                   </object>
                   <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
+                    <property name="top_attach">2</property>
+                    <property name="left_attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
                   </packing>
                 </child>
                 <child>
@@ -8408,11 +8458,13 @@ Check this if you want the queue to clean itself up by deleting completed jobs.<
                     <property name="truncate_multiline">True</property>
                     <property name="primary_icon_activatable">False</property>
                     <property name="secondary_icon_activatable">False</property>
+                    <signal name="changed" handler="preset_name_changed_cb" swapped="no"/>
                   </object>
                   <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
+                    <property name="top_attach">2</property>
+                    <property name="left_attach">1</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
                   </packing>
                 </child>
               </object>
index c3883595903346eed1697eb617a1c776230797d3..3f7b1fd680d73bef37ae3ba2f393429fa9724575 100644 (file)
@@ -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)
 {
index be47bfbc41b2ffabb7bd4740f8d9c2f85daa5eac..77a5df2c33c5d831c7af04e796047df4a51d046f 100644 (file)
@@ -41,6 +41,7 @@
         "MetaDescription": "",
         "MetaLongDescription": "",
         "PresetFullName": "/Regular/Normal",
+        "PresetCategory": "new",
         "preset_modified": false,
         "preset_reload": false,
         "PictureDisplayWidth": 720,
index aacb72d5c364fb408e693a50ff6223068d044b27..8270a58f531b922ba18ed30c10f202b056fb351f 100644 (file)
@@ -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
     {
index 7598984005bb8f340053b8e01d46c70e226e051f..69606aa84eda9793d00c36ae99d2717262c1d1eb 100644 (file)
@@ -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():
index 955d9f9db66310af82f3e0b5731e1d0f932f2443..87ca958605762928423cb116c82e6b299c0786ec 100644 (file)
 
 #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;
index b711373752046c12e72d64f1bb954f258b2bf9ee..cd14d48ce78d108ee80ebbfdbbc7b82bab6fdf9f 100644 (file)
@@ -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);
index af94d853914be3ca78545e8058169a5d2f451f07..19a013211944e101b4c321a52712eccc5d387915 100644 (file)
@@ -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);
index e797edeb602b9423b89df0e777c4c8ac0a243e83..61ffaf9b6fcfc246f31dbf0509e8f31ccbafa30a 100644 (file)
 
 #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);
index e2336dab0576bf84473cc6eefb377970a762b1af..1e3238989a3df2bc4103f3a92d75114126892816 100644 (file)
@@ -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"