From 8e1209d464ba70a76c5fb92977ec6bfb70c8f9fb Mon Sep 17 00:00:00 2001 From: Patrick McDermott Date: Wed, 01 Nov 2017 12:54:02 -0400 Subject: src/config.[ch], src/settings.[ch]: Move under src/config/ --- (limited to 'src/config/config.c') diff --git a/src/config/config.c b/src/config/config.c new file mode 100644 index 0000000..8b46fb1 --- /dev/null +++ b/src/config/config.c @@ -0,0 +1,529 @@ +/* + * Application configuration + * + * Copyright (C) 2017 Patrick McDermott + * + * This file is part of Marquee. + * + * Marquee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Marquee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Marquee. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "config.h" + +#include +#include + +#include + +struct MqConfig { + gchar *profile; + gchar *file_name; + GKeyFile *key_file; + gboolean types_and_cbs_set; + GHashTable *types_and_cbs; +}; + +enum type { + TYPE_BOOLEAN, + TYPE_INTEGER, + TYPE_DOUBLE, + TYPE_STRING, +}; + +union callback { + MqConfigBooleanCallback boolean_cb; + MqConfigIntegerCallback integer_cb; + MqConfigDoubleCallback double_cb; + MqConfigStringCallback string_cb; +}; + +struct callbacks { + union callback cb; + gpointer user_data; + struct callbacks *next; +}; + +struct item { + enum type type; + struct callbacks *callbacks; +}; + +#define SET_BOOL(NAME, VALUE) mq_config_set_boolean(config, NAME, VALUE) +#define SET_INT( NAME, VALUE) mq_config_set_integer(config, NAME, VALUE) +#define SET_DBL( NAME, VALUE) mq_config_set_double (config, NAME, VALUE) +#define SET_STR( NAME, VALUE) mq_config_set_string (config, NAME, VALUE) +#define SET_STRL(NAME, VALUE) mq_config_set_string (config, NAME, VALUE) + +static void +set_defaults(MqConfig *config) +{ + config->types_and_cbs_set = FALSE; + + /* General -> Web Browsing */ + SET_STR ("tabs.new", "home"); + SET_STR ("tabs.home", ""); /* TODO */ + SET_BOOL("tabs.background", TRUE); + SET_BOOL("tabs.warn-on-close", TRUE); + + /* General -> Navigation and Accessibility */ + SET_BOOL("navigation.smooth-scrolling", TRUE); + SET_BOOL("navigation.tabbing", TRUE); + SET_BOOL("navigation.caret", FALSE); + SET_BOOL("navigation.spatial", FALSE); + + /* General -> Spell Checking */ + SET_BOOL("spell.enable", TRUE); + SET_STRL("spell.langs", "en_US"); // TODO + + /* General -> Miscellaneous */ + SET_BOOL("display.textarea.resize.enable", TRUE); + SET_BOOL("devtools.enable", TRUE); + + /* General -> Compatibility */ + SET_STR ("compatibility.user-agent", "default"); + SET_BOOL("compatibility.quirks", TRUE); + + /* Display -> Fonts */ + SET_STR ("font.family.default", "sans-serif"); + SET_STR ("font.family.monospace", "monospace"); + SET_STR ("font.family.serif", "serif"); + SET_STR ("font.family.sans-serif", "sans-serif"); + SET_STR ("font.family.cursive", "serif"); + SET_STR ("font.family.fantasy", "serif"); + SET_STR ("font.family.pictograph", "serif"); + SET_INT ("font.size.default", 16); + SET_INT ("font.size.monospace-default", 13); + SET_INT ("font.size.minimum", 0); + + /* Display -> Languages */ + SET_STRL("intl.accept-languages", "en-us"); // TODO + SET_STR ("intl.default-charset", "iso-8859-1"); //^ + + /* Display -> Zoom */ + SET_DBL ("zoom.default", 1.00); + SET_BOOL("zoom.text-only", FALSE); + + /* Permissions -> General */ + SET_BOOL("permissions.images.auto-load", TRUE); + SET_BOOL("permissions.images.favicons.override", FALSE); + SET_BOOL("permissions.java.enable", TRUE); + SET_BOOL("permissions.javascript.enable", TRUE); + SET_BOOL("permissions.plugins.enable", TRUE); + + /* Permissions -> JavaScript */ + SET_BOOL("permissions.javascript.open-windows", FALSE); + SET_BOOL("permissions.javascript.fullscreen", TRUE); + SET_BOOL("permissions.javascript.modal-dialogs", FALSE); + SET_BOOL("permissions.javascript.clipboard", FALSE); + + /* Permissions -> Data Storage */ + SET_BOOL("permissions.javascript.database", TRUE); + SET_BOOL("permissions.javascript.storage", TRUE); + SET_BOOL("permissions.appcache", TRUE); + + /* Permissions -> Graphics and Multimedia */ + SET_BOOL("canvas.acceleration.enable", FALSE); + SET_BOOL("permissions.javascript.webgl", FALSE); + SET_BOOL("permissions.javascript.audio", FALSE); + SET_BOOL("media.autoplay", TRUE); + SET_BOOL("media.force-fullscreen", FALSE); + SET_BOOL("permissions.javascript.mediastream.enable", FALSE); + SET_BOOL("permissions.javascript.mediasource.enable", FALSE); + + /* Security and Privacy -> History */ + SET_BOOL("privacy.private-browsing.enabled", FALSE); + SET_BOOL("privacy.remember.history", TRUE); + SET_BOOL("privacy.remember.downloads", TRUE); + + /* Security and Privacy -> Cookies */ + SET_STR ("cookies.accept", "always"); + + /* Security and Privacy -> Security */ + SET_BOOL("security.xss-auditor.enable", TRUE); + + /* Security and Privacy -> Network */ + SET_BOOL("dns.prefetch.enable", FALSE); + + /* Hidden window state preferences */ + SET_BOOL("window.maximized", FALSE); + SET_INT ("window.width", 1024); + SET_INT ("window.height", 768); + + /* Hidden directory preferences */ + SET_STR ("directories.open-file", ""); + SET_STR ("directories.downloads", + g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD)); + + config->types_and_cbs_set = TRUE; +} + +#undef SET_BOOL +#undef SET_INT +#undef SET_DBL +#undef SET_STR +#undef SET_STRL + +MqConfig * +mq_config_new(const gchar *profile) +{ + MqConfig *config; + + config = malloc(sizeof(*config)); + + config->profile = g_strdup(profile); + config->file_name = g_build_filename(g_get_user_config_dir(), + PACKAGE, profile, "config", NULL); + config->key_file = g_key_file_new(); + config->types_and_cbs = g_hash_table_new(g_str_hash, g_int_equal); + set_defaults(config); + + return config; +} + +gboolean +mq_config_load(MqConfig *config) +{ + gchar *profile_dir; + + profile_dir = g_build_filename(g_get_user_config_dir(), PACKAGE, + config->profile, NULL); + g_mkdir_with_parents(profile_dir, 0700); + g_free(profile_dir); + + /* TODO: Handle parsing and ENOENT errors differently? */ + return g_key_file_load_from_file(config->key_file, config->file_name, + G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL); +} + +gboolean +mq_config_save(MqConfig *config) +{ + /* TODO: Handle GFileError? */ + return g_key_file_save_to_file(config->key_file, config->file_name, + NULL); +} + +static void +split_name(const gchar *name, gchar **group, gchar **key) +{ + *group = g_strdup(name); + *key = strchr(*group, '.'); + *(key[0]) = '\0'; + ++*key; +} + +gboolean +mq_config_get_boolean(MqConfig *config, const gchar *name) +{ + gchar *group; + gchar *key; + gboolean value; + + split_name(name, &group, &key); + + /* TODO: Handle value parsing errors? */ + value = g_key_file_get_boolean(config->key_file, group, key, NULL); + + g_free(group); + + return value; +} + +gint +mq_config_get_integer(MqConfig *config, const gchar *name) +{ + gchar *group; + gchar *key; + gint value; + + split_name(name, &group, &key); + + /* TODO: Handle value parsing errors? */ + value = g_key_file_get_integer(config->key_file, group, key, NULL); + + g_free(group); + + return value; +} + +gdouble +mq_config_get_double(MqConfig *config, const gchar *name) +{ + gchar *group; + gchar *key; + gdouble value; + + split_name(name, &group, &key); + + /* TODO: Handle value parsing errors? */ + value = g_key_file_get_double(config->key_file, group, key, NULL); + + g_free(group); + + return value; +} + +gchar * +mq_config_get_string(MqConfig *config, const gchar *name) +{ + gchar *group; + gchar *key; + gchar *value; + + split_name(name, &group, &key); + + /* TODO: Handle value parsing errors? */ + value = g_key_file_get_string(config->key_file, group, key, NULL); + + g_free(group); + + return value; +} + +gchar ** +mq_config_get_string_list(MqConfig *config, const gchar *name) +{ + gchar *group; + gchar *key; + gchar **value; + + split_name(name, &group, &key); + + /* TODO: Handle value parsing errors? */ + value = g_key_file_get_string_list(config->key_file, group, key, NULL, + NULL); + + g_free(group); + + return value; +} + +static void +set_type_or_run_callbacks(MqConfig *config, const gchar *name, + gconstpointer value, enum type type) +{ + struct item *item; + struct callbacks *cbs; + + if (!config->types_and_cbs_set) { + item = g_malloc(sizeof(*item)); + item->type = type; + item->callbacks = NULL; + g_hash_table_insert(config->types_and_cbs, g_strdup(name), + item); + } else { + item = g_hash_table_lookup(config->types_and_cbs, name); + for (cbs = item->callbacks; cbs; cbs = cbs->next) { + switch (item->type) { + case TYPE_BOOLEAN: + cbs->cb.boolean_cb(config, name, + *((const gboolean *) value), + cbs->user_data); + break; + case TYPE_INTEGER: + cbs->cb.integer_cb(config, name, + *((const gint *) value), + cbs->user_data); + break; + case TYPE_DOUBLE: + cbs->cb.double_cb(config, name, + *((const gdouble *) value), + cbs->user_data); + break; + case TYPE_STRING: + cbs->cb.string_cb(config, name, + (const gchar *) value, + cbs->user_data); + break; + } + } + } +} + +void +mq_config_set_boolean(MqConfig *config, const gchar *name, gboolean value) +{ + gchar *group; + gchar *key; + + split_name(name, &group, &key); + + g_key_file_set_boolean(config->key_file, group, key, value); + + g_free(group); + + set_type_or_run_callbacks(config, name, &value, TYPE_BOOLEAN); +} + +void +mq_config_set_integer(MqConfig *config, const gchar *name, gint value) +{ + gchar *group; + gchar *key; + + split_name(name, &group, &key); + + g_key_file_set_integer(config->key_file, group, key, value); + + g_free(group); + + set_type_or_run_callbacks(config, name, &value, TYPE_INTEGER); +} + +void +mq_config_set_double(MqConfig *config, const gchar *name, gdouble value) +{ + gchar *group; + gchar *key; + + split_name(name, &group, &key); + + g_key_file_set_double(config->key_file, group, key, value); + + g_free(group); + + set_type_or_run_callbacks(config, name, &value, TYPE_DOUBLE); +} + +void +mq_config_set_string(MqConfig *config, const gchar *name, const gchar *value) +{ + gchar *group; + gchar *key; + + split_name(name, &group, &key); + + g_key_file_set_string(config->key_file, group, key, value); + + g_free(group); + + set_type_or_run_callbacks(config, name, value, TYPE_STRING); +} + +gboolean +mq_config_set(MqConfig *config, const gchar *name, const gchar *value) +{ + struct item *item; + gint integer_value; + gdouble double_value; + gchar *endptr; + + item = g_hash_table_lookup(config->types_and_cbs, name); + g_assert(item); + + switch (item->type) { + case TYPE_BOOLEAN: + /* value is "on" or "off" (as implemented in + * mq_html_input_checkbox()). */ + mq_config_set_boolean(config, name, + value[1] == 'n' ? TRUE : FALSE); + return TRUE; + case TYPE_INTEGER: + integer_value = strtol(value, &endptr, 10); + if (*endptr != '\0') { + return FALSE; + } + mq_config_set_integer(config, name, integer_value); + return TRUE; + case TYPE_DOUBLE: + double_value = strtod(value, &endptr); + if (*endptr != '\0') { + return FALSE; + } + mq_config_set_double(config, name, double_value); + return TRUE; + case TYPE_STRING: + mq_config_set_string(config, name, value); + return TRUE; + } + + g_assert_not_reached(); + return FALSE; +} + +void +mq_config_notify_boolean(MqConfig *config, const gchar *name, + MqConfigBooleanCallback callback, gpointer user_data) +{ + struct item *item; + struct callbacks *cbs; + + item = g_hash_table_lookup(config->types_and_cbs, name); + g_assert(item->type == TYPE_BOOLEAN); + + cbs = g_malloc(sizeof(*cbs)); + cbs->cb.boolean_cb = callback; + cbs->user_data = user_data; + cbs->next = item->callbacks; + + item->callbacks = cbs; +} + +void +mq_config_notify_integer(MqConfig *config, const gchar *name, + MqConfigIntegerCallback callback, gpointer user_data) +{ + struct item *item; + struct callbacks *cbs; + + item = g_hash_table_lookup(config->types_and_cbs, name); + g_assert(item->type == TYPE_INTEGER); + + cbs = g_malloc(sizeof(*cbs)); + cbs->cb.integer_cb = callback; + cbs->user_data = user_data; + cbs->next = item->callbacks; + + item->callbacks = cbs; +} + +void +mq_config_notify_double(MqConfig *config, const gchar *name, + MqConfigDoubleCallback callback, gpointer user_data) +{ + struct item *item; + struct callbacks *cbs; + + item = g_hash_table_lookup(config->types_and_cbs, name); + g_assert(item->type == TYPE_DOUBLE); + + cbs = g_malloc(sizeof(*cbs)); + cbs->cb.double_cb = callback; + cbs->user_data = user_data; + cbs->next = item->callbacks; + + item->callbacks = cbs; +} + +void +mq_config_notify_string(MqConfig *config, const gchar *name, + MqConfigStringCallback callback, gpointer user_data) +{ + struct item *item; + struct callbacks *cbs; + + item = g_hash_table_lookup(config->types_and_cbs, name); + g_assert(item->type == TYPE_STRING); + + cbs = g_malloc(sizeof(*cbs)); + cbs->cb.string_cb = callback; + cbs->user_data = user_data; + cbs->next = item->callbacks; + + item->callbacks = cbs; +} -- cgit v0.9.1