// SPDX-FileCopyrightText: © 2018-2022 Alexandros Theodotou // SPDX-License-Identifier: LicenseRef-ZrythmLicense /** * \file * * Application settings. */ #include "zrythm-config.h" #include #include #include "settings/plugin_settings.h" #include "settings/settings.h" #include "settings/user_shortcuts.h" #include "utils/gtk.h" #include "utils/mem.h" #include "utils/objects.h" #include "utils/string.h" #include "utils/strv_builder.h" #include "utils/terminal.h" #include "zrythm.h" #include #include #define G_SETTINGS_ENABLE_BACKEND #include /** * Initializes settings. */ Settings * settings_new (void) { Settings * self = object_new (Settings); if (ZRYTHM_TESTING) { return self; } self->general = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".general"); self->export_audio = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".export.audio"); self->export_midi = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".export.midi"); self->monitor = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".monitor"); self->ui = g_settings_new (GSETTINGS_ZRYTHM_PREFIX ".ui"); self->ui_inspector = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".ui.inspector"); self->ui_mixer = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".ui.mixer"); self->ui_plugin_browser = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".ui.plugin-browser"); self->ui_panels = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".ui.panels"); self->ui_file_browser = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".ui.file-browser"); self->transport = g_settings_new ( GSETTINGS_ZRYTHM_PREFIX ".transport"); #define PREFERENCES_PREFIX \ GSETTINGS_ZRYTHM_PREFIX ".preferences." #define NEW_PREFERENCES_SETTINGS(a, b) \ self->preferences_##a##_##b = g_settings_new ( \ PREFERENCES_PREFIX #a "." #b); \ g_return_val_if_fail ( \ self->preferences_##a##_##b, NULL) NEW_PREFERENCES_SETTINGS (dsp, pan); NEW_PREFERENCES_SETTINGS (editing, audio); NEW_PREFERENCES_SETTINGS (editing, automation); NEW_PREFERENCES_SETTINGS (editing, undo); NEW_PREFERENCES_SETTINGS (general, engine); NEW_PREFERENCES_SETTINGS (general, paths); NEW_PREFERENCES_SETTINGS (general, updates); NEW_PREFERENCES_SETTINGS (plugins, uis); NEW_PREFERENCES_SETTINGS (plugins, paths); NEW_PREFERENCES_SETTINGS (projects, general); NEW_PREFERENCES_SETTINGS (ui, general); NEW_PREFERENCES_SETTINGS (scripting, general); #undef PREFERENCES_PREFIX g_return_val_if_fail ( self->general && self->export_audio && self->ui && self->ui_inspector, NULL); #if 0 /* plugin settings */ GSettingsBackend * psettings_backend = g_keyfile_settings_backend_new ( "/filesystem/path/plugin-settings.ini", "/", NULL); self->plugin_settings = g_settings_new_with_backend ( GSETTINGS_ZRYTHM_PREFIX ".plugin-settings", psettings_backend); g_settings_set_boolean ( self->plugin_settings, "loop", 1); #endif self->plugin_settings = plugin_settings_new (); g_return_val_if_fail ( self->plugin_settings, NULL); self->user_shortcuts = user_shortcuts_new (); if (!self->user_shortcuts) { g_warning ("failed to parse user shortcuts"); } return self; } static int print_or_reset_schema ( GSettingsSchema * schema, const char * schema_str, int print_only, int pretty_print) { GSettings * settings = g_settings_new (schema_str); char ** keys = g_settings_schema_list_keys (schema); char tmp[8000]; if (print_only) { sprintf ( tmp, _ ("Schema %s%s%s"), pretty_print ? TERMINAL_BOLD : "", schema_str, pretty_print ? TERMINAL_RESET : ""); printf ( "%s%s\n", pretty_print ? TERMINAL_UNDERLINE : "", tmp); } else { printf ( _ ("Resetting schema %s...\n"), schema_str); } int i = 0; char * key; while ((key = keys[i++])) { GSettingsSchemaKey * schema_key = g_settings_schema_get_key (schema, key); g_return_val_if_fail (schema_key, -1); if (print_only) { GVariant * val = g_settings_get_value (settings, key); char * var_str = g_variant_print (val, 0); printf ( " %s%s%s=%s%s%s\n", pretty_print ? TERMINAL_COLOR_MAGENTA TERMINAL_BOLD : "", key, pretty_print ? TERMINAL_RESET : "", pretty_print ? TERMINAL_COLOR_YELLOW : "", var_str, pretty_print ? TERMINAL_RESET : ""); if (pretty_print) { const char * description = g_settings_schema_key_get_description ( schema_key); printf (" %s\n", _ (description)); } } else if ( /* don't reset install-dir */ string_is_equal (key, "install-dir")) { sprintf (tmp, _ ("skipping %s"), key); printf (" %s\n", tmp); } else { GVariant * val = g_settings_schema_key_get_default_value ( schema_key); g_return_val_if_fail (val, -1); char * var_str = g_variant_print (val, 0); sprintf ( tmp, _ ("resetting %s to %s"), key, var_str); printf (" %s\n", tmp); int ret = g_settings_set_value ( settings, key, val); if (!ret) { g_warning ("Failed to set value"); return -1; } } } return 0; } /** * @param print_only Only print the settings without * resetting. */ static int print_or_reset ( int print_only, int pretty_print, int exit_on_finish) { GSettingsSchemaSource * source = g_settings_schema_source_get_default (); char ** non_relocatable; g_settings_schema_source_list_schemas ( source, 1, &non_relocatable, NULL); int i = 0; char * schema_str; while ((schema_str = non_relocatable[i++])) { if (string_contains_substr ( schema_str, GSETTINGS_ZRYTHM_PREFIX)) { GSettingsSchema * schema = g_settings_schema_source_lookup ( source, schema_str, 1); if (!schema) { fprintf ( stderr, _ ("Failed to find schema %s. %s is likely not installed"), schema_str, PROGRAM_NAME); if (exit_on_finish) exit (1); else return -1; } if (print_or_reset_schema ( schema, schema_str, print_only, pretty_print)) { if (print_only) { fprintf ( stderr, _ ("Failed to view schema %s"), schema_str); } else { fprintf ( stderr, _ ("Failed to reset schema %s"), schema_str); } if (exit_on_finish) exit (1); else return -1; } } } if (!print_only) { g_settings_sync (); } return 0; } /** * Resets settings to defaults. * * @param window Window to set transient to if * confirming, otherwise console confirmation will * be used. * @param exit_on_finish Exit with a code on * finish. * * @return Whether successfully reset. */ bool settings_reset_to_factory ( bool confirm, GtkWindow * window, bool exit_on_finish) { if (confirm) { if (window) { GtkDialog * dialog = GTK_DIALOG ( gtk_message_dialog_new_with_markup ( window, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL, NULL)); gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG (dialog), _ ("This will reset Zrythm to " "factory settings. You will lose " "all your preferences. " "Continue?")); gtk_window_set_title ( GTK_WINDOW (dialog), _ ("Reset to Factory Settings")); int response = z_gtk_dialog_run (dialog, true); if (response != GTK_RESPONSE_OK) return false; } else { printf ( "%s ", _ ("This will reset Zrythm to factory " "settings. You will lose all your " "preferences. " "Type 'y' to continue:")); char c = getchar (); if (c != 'y') { printf (_ ("Aborting...\n")); if (exit_on_finish) exit (0); return false; } } } print_or_reset (0, 0, exit_on_finish); printf ( _ ("Reset to factory settings successful\n")); return true; } /** * Prints the current settings. */ void settings_print (int pretty_print) { print_or_reset (1, pretty_print, 0); } GVariant * settings_get_range ( const char * schema, const char * key) { GSettingsSchemaSource * source = g_settings_schema_source_get_default (); GSettingsSchema * g_settings_schema = g_settings_schema_source_lookup ( source, schema, 1); GSettingsSchemaKey * schema_key = g_settings_schema_get_key ( g_settings_schema, key); GVariant * range = g_settings_schema_key_get_range (schema_key); g_settings_schema_key_unref (schema_key); g_settings_schema_unref (g_settings_schema); return range; } GVariant * settings_get_default_value ( const char * schema, const char * key) { GSettingsSchemaSource * source = g_settings_schema_source_get_default (); GSettingsSchema * g_settings_schema = g_settings_schema_source_lookup ( source, schema, 1); GSettingsSchemaKey * schema_key = g_settings_schema_get_key ( g_settings_schema, key); GVariant * def_val = g_settings_schema_key_get_default_value ( schema_key); g_settings_schema_key_unref (schema_key); g_settings_schema_unref (g_settings_schema); return def_val; } double settings_get_default_value_double ( const char * schema, const char * key) { GVariant * def_val = settings_get_default_value (schema, key); double ret = g_variant_get_double (def_val); g_variant_unref (def_val); return ret; } void settings_get_range_double ( const char * schema, const char * key, double * lower, double * upper) { GVariant * range = settings_get_range (schema, key); GVariant * range_vals = g_variant_get_child_value (range, 1); range_vals = g_variant_get_child_value (range_vals, 0); GVariant * lower_var = g_variant_get_child_value (range_vals, 0); GVariant * upper_var = g_variant_get_child_value (range_vals, 1); *lower = g_variant_get_double (lower_var); *upper = g_variant_get_double (upper_var); g_variant_unref (range_vals); g_variant_unref (lower_var); g_variant_unref (upper_var); } /** * Returns whether the "as" key contains the given * string. */ bool settings_strv_contains_str ( GSettings * settings, const char * key, const char * val) { char ** strv = g_settings_get_strv (settings, key); for (int i = 0;; i++) { char * str = strv[i]; if (!str) break; if (string_is_equal (str, val)) { g_strfreev (strv); return true; } } g_strfreev (strv); return false; } /** * Appends the given string to a key of type "as". */ void settings_append_to_strv ( GSettings * settings, const char * key, const char * val, bool ignore_if_duplicate) { if ( ignore_if_duplicate && !ZRYTHM_TESTING && settings_strv_contains_str ( settings, key, val)) { return; } g_debug ( "%s: key %s val %s ignore if duplicate %d", __func__, key, val, ignore_if_duplicate); StrvBuilder * builder = strv_builder_new (); char ** strv = ZRYTHM_TESTING ? NULL : g_settings_get_strv (settings, key); if (strv) strv_builder_addv ( builder, (const char **) strv); strv_builder_add (builder, val); char ** new_strv = strv_builder_end (builder); if (ZRYTHM_TESTING) { for (size_t i = 0; new_strv[i] != NULL; i++) { char * tmp = new_strv[i]; g_message ("setting [%zu]: %s", i, tmp); } } else { g_settings_set_strv ( settings, key, (const char * const *) new_strv); } g_strfreev (strv); g_strfreev (new_strv); } /** * Frees settings. */ void settings_free (Settings * self) { #define FREE_SETTING(x) \ g_object_unref_and_null (self->x) FREE_SETTING (general); FREE_SETTING (preferences_dsp_pan); FREE_SETTING (preferences_editing_audio); FREE_SETTING (preferences_editing_automation); FREE_SETTING (preferences_editing_undo); FREE_SETTING (preferences_general_engine); FREE_SETTING (preferences_general_paths); FREE_SETTING (preferences_general_updates); FREE_SETTING (preferences_plugins_uis); FREE_SETTING (preferences_plugins_paths); FREE_SETTING (preferences_projects_general); FREE_SETTING (preferences_ui_general); FREE_SETTING (preferences_scripting_general); FREE_SETTING (monitor); FREE_SETTING (ui); FREE_SETTING (transport); FREE_SETTING (export_audio); FREE_SETTING (export_midi); FREE_SETTING (ui_mixer); FREE_SETTING (ui_inspector); FREE_SETTING (ui_panels); FREE_SETTING (ui_plugin_browser); FREE_SETTING (ui_file_browser); #undef FREE_SETTING object_free_w_func_and_null ( plugin_settings_free, self->plugin_settings); object_free_w_func_and_null ( user_shortcuts_free, self->user_shortcuts); object_zero_and_free (self); }