From 834a3f2151dd8738a1f878489f6207664c4af5aa Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sun, 14 Jul 2013 17:14:53 -0700 Subject: Imported Upstream version 1.1.1 --- src/trg-prefs.c | 578 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 578 insertions(+) create mode 100644 src/trg-prefs.c (limited to 'src/trg-prefs.c') diff --git a/src/trg-prefs.c b/src/trg-prefs.c new file mode 100644 index 0000000..b559479 --- /dev/null +++ b/src/trg-prefs.c @@ -0,0 +1,578 @@ +/* + * transmission-remote-gtk - A GTK RPC client to Transmission + * Copyright (C) 2011-2013 Alan Fitton + + * This program 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 2 of the License, or + * (at your option) any later version. + + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "torrent.h" +#include "trg-client.h" +#include "trg-prefs.h" + +/* I replaced GConf with this custom configuration backend for a few reasons. + * 1) Better windows support. No dependency on DBus. + * 2) We're including a JSON parser/writer anyway. + * 3) The GConf API is pretty verbose sometimes, working with lists is awkward. + * 4) Easily switch between profiles, and scope config to either profiles or global. + * 5) Transparently use configurables from the current connection, not current profile. + */ + +G_DEFINE_TYPE(TrgPrefs, trg_prefs, G_TYPE_OBJECT) + +struct _TrgPrefsPrivate { + JsonObject *defaultsObj; + JsonNode *user; + JsonObject *userObj; + JsonObject *connectionObj; + JsonObject *profile; + gchar *file; +}; + +enum { + PREF_CHANGE, + PREF_PROFILE_CHANGE, + PREFS_SIGNAL_COUNT +}; + +static guint signals[PREFS_SIGNAL_COUNT] = { 0 }; + +void trg_prefs_profile_change_emit_signal(TrgPrefs * p) +{ + g_signal_emit(p, signals[PREF_PROFILE_CHANGE], 0); +} + +void trg_prefs_changed_emit_signal(TrgPrefs * p, const gchar * key) +{ + g_signal_emit(p, signals[PREF_CHANGE], 0, key); +} + +static void +trg_prefs_get_property(GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +} + +static void +trg_prefs_set_property(GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); +} + +static void trg_prefs_dispose(GObject * object) +{ + G_OBJECT_CLASS(trg_prefs_parent_class)->dispose(object); +} + +static void trg_prefs_create_defaults(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + priv->defaultsObj = json_object_new(); + + trg_prefs_add_default_string(p, TRG_PREFS_KEY_PROFILE_NAME, + _(TRG_PROFILE_NAME_DEFAULT)); + trg_prefs_add_default_string(p, TRG_PREFS_KEY_RPC_URL_PATH, "/transmission/rpc"); + trg_prefs_add_default_int(p, TRG_PREFS_KEY_PORT, 9091); + trg_prefs_add_default_int(p, TRG_PREFS_KEY_UPDATE_INTERVAL, + TRG_INTERVAL_DEFAULT); + trg_prefs_add_default_int(p, TRG_PREFS_KEY_SESSION_UPDATE_INTERVAL, + TRG_SESSION_INTERVAL_DEFAULT); + trg_prefs_add_default_int(p, TRG_PREFS_KEY_MINUPDATE_INTERVAL, + TRG_INTERVAL_DEFAULT); + trg_prefs_add_default_int(p, TRG_PREFS_ACTIVEONLY_FULLSYNC_EVERY, 2); + trg_prefs_add_default_int(p, TRG_PREFS_KEY_STATES_PANED_POS, 120); + trg_prefs_add_default_int(p, TRG_PREFS_KEY_TIMEOUT, 40); + trg_prefs_add_default_int(p, TRG_PREFS_KEY_RETRIES, 3); + + trg_prefs_add_default_bool_true(p, TRG_PREFS_KEY_FILTER_DIRS); + trg_prefs_add_default_bool_true(p, TRG_PREFS_KEY_FILTER_TRACKERS); + trg_prefs_add_default_bool_true(p, TRG_PREFS_KEY_SHOW_GRAPH); + trg_prefs_add_default_bool_true(p, TRG_PREFS_KEY_ADD_OPTIONS_DIALOG); + trg_prefs_add_default_bool_true(p, TRG_PREFS_KEY_SHOW_STATE_SELECTOR); + //trg_prefs_add_default_bool_true(p, TRG_PREFS_KEY_SHOW_NOTEBOOK); +} + +static GObject *trg_prefs_constructor(GType type, + guint n_construct_properties, + GObjectConstructParam * + construct_params) +{ + GObject *object = G_OBJECT_CLASS + (trg_prefs_parent_class)->constructor(type, n_construct_properties, + construct_params); + TrgPrefs *prefs = TRG_PREFS(object); + TrgPrefsPrivate *priv = prefs->priv; + + trg_prefs_create_defaults(prefs); + + priv->file = g_build_filename(g_get_user_config_dir(), + g_get_application_name(), + TRG_PREFS_FILENAME, NULL); + + return object; +} + +static void trg_prefs_class_init(TrgPrefsClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + g_type_class_add_private(klass, sizeof(TrgPrefsPrivate)); + + object_class->get_property = trg_prefs_get_property; + object_class->set_property = trg_prefs_set_property; + object_class->dispose = trg_prefs_dispose; + object_class->constructor = trg_prefs_constructor; + + signals[PREF_CHANGE] = + g_signal_new("pref-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(TrgPrefsClass, + pref_changed), NULL, + NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[PREF_PROFILE_CHANGE] = + g_signal_new("pref-profile-changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(TrgPrefsClass, + pref_changed), NULL, + NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +static void trg_prefs_init(TrgPrefs * self) +{ + self->priv = + G_TYPE_INSTANCE_GET_PRIVATE(self, TRG_TYPE_PREFS, TrgPrefsPrivate); +} + +TrgPrefs *trg_prefs_new(void) +{ + return g_object_new(TRG_TYPE_PREFS, NULL); +} + +static JsonObject *trg_prefs_new_profile_object() +{ + return json_object_new(); +} + +void trg_prefs_add_default_int(TrgPrefs * p, const gchar * key, int value) +{ + TrgPrefsPrivate *priv = p->priv; + + json_object_set_int_member(priv->defaultsObj, key, value); +} + +void +trg_prefs_add_default_string(TrgPrefs * p, const gchar * key, + gchar * value) +{ + TrgPrefsPrivate *priv = p->priv; + + json_object_set_string_member(priv->defaultsObj, key, value); +} + +void +trg_prefs_add_default_double(TrgPrefs * p, const gchar * key, double value) +{ + TrgPrefsPrivate *priv = p->priv; + + json_object_set_double_member(priv->defaultsObj, key, value); +} + +/* Not much point adding a default of FALSE, as that's the fallback */ +void trg_prefs_add_default_bool_true(TrgPrefs * p, const gchar * key) +{ + TrgPrefsPrivate *priv = p->priv; + json_object_set_boolean_member(priv->defaultsObj, key, TRUE); +} + +gint trg_prefs_get_profile_id(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + return (gint) json_object_get_int_member(priv->userObj, + TRG_PREFS_KEY_PROFILE_ID); +} + +static JsonNode *trg_prefs_get_value_inner(JsonObject * obj, + const gchar * key, int type, + int flags) +{ + if (json_object_has_member(obj, key)) { + if ((flags & TRG_PREFS_REPLACENODE)) + json_object_remove_member(obj, key); + else + return json_object_get_member(obj, key); + } + + if ((flags & TRG_PREFS_NEWNODE) || (flags & TRG_PREFS_REPLACENODE)) { + JsonNode *newNode = json_node_new(type); + json_object_set_member(obj, key, newNode); + return newNode; + } + + return NULL; +} + +JsonNode *trg_prefs_get_value(TrgPrefs * p, const gchar * key, int type, + int flags) +{ + TrgPrefsPrivate *priv = p->priv; + JsonNode *res; + + if (priv->profile && (flags & TRG_PREFS_PROFILE)) { + if ((res = + trg_prefs_get_value_inner(priv->profile, key, type, flags))) + return res; + } else if (priv->connectionObj && (flags & TRG_PREFS_CONNECTION)) { + if ((res = + trg_prefs_get_value_inner(priv->connectionObj, key, type, + flags))) + return res; + } else { + if ((res = + trg_prefs_get_value_inner(priv->userObj, key, type, flags))) + return res; + } + + if (priv->defaultsObj + && json_object_has_member(priv->defaultsObj, key)) + return json_object_get_member(priv->defaultsObj, key); + + return NULL; +} + +void trg_prefs_set_connection(TrgPrefs * p, JsonObject * profile) +{ + TrgPrefsPrivate *priv = p->priv; + + if (priv->connectionObj) + json_object_unref(priv->connectionObj); + + if (profile) + json_object_ref(profile); + + priv->connectionObj = profile; +} + +gchar *trg_prefs_get_string(TrgPrefs * p, const gchar * key, int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_VALUE, flags); + + if (node) + return g_strdup(json_node_get_string(node)); + else + return NULL; +} + +JsonArray *trg_prefs_get_array(TrgPrefs * p, const gchar * key, int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_ARRAY, flags); + + if (node) + return json_node_get_array(node); + else + return NULL; +} + +gint64 trg_prefs_get_int(TrgPrefs * p, const gchar * key, int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_VALUE, flags); + + if (node) + return json_node_get_int(node); + else + return 0; +} + +gdouble trg_prefs_get_double(TrgPrefs * p, const gchar * key, int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_VALUE, flags); + if (node) + return json_node_get_double(node); + else + return 0.0; +} + +gboolean trg_prefs_get_bool(TrgPrefs * p, const gchar * key, int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_VALUE, flags); + + if (node) + return json_node_get_boolean(node); + else + return FALSE; +} + +void +trg_prefs_set_int(TrgPrefs * p, const gchar * key, int value, int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_VALUE, + flags | TRG_PREFS_NEWNODE); + + json_node_set_int(node, (gint64) value); + trg_prefs_changed_emit_signal(p, key); +} + +void +trg_prefs_set_string(TrgPrefs * p, const gchar * key, + const gchar * value, int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_VALUE, + flags | TRG_PREFS_NEWNODE); + json_node_set_string(node, value); + trg_prefs_changed_emit_signal(p, key); +} + +void trg_prefs_set_profile(TrgPrefs * p, JsonObject * profile) +{ + TrgPrefsPrivate *priv = p->priv; + GList *profiles = json_array_get_elements(trg_prefs_get_profiles(p)); + gint i = 0; + GList *li; + + priv->profile = profile; + + for (li = profiles; li; li = g_list_next(li)) { + if (json_node_get_object((JsonNode *) li->data) == profile) { + trg_prefs_set_int(p, TRG_PREFS_KEY_PROFILE_ID, i, + TRG_PREFS_GLOBAL); + break; + } + i++; + } + + g_list_free(profiles); + + trg_prefs_changed_emit_signal(p, NULL); + trg_prefs_profile_change_emit_signal(p); +} + +JsonObject *trg_prefs_new_profile(TrgPrefs * p) +{ + JsonArray *profiles = trg_prefs_get_profiles(p); + JsonObject *newp = trg_prefs_new_profile_object(); + + json_array_add_object_element(profiles, newp); + + return newp; +} + +void trg_prefs_del_profile(TrgPrefs * p, JsonObject * profile) +{ + JsonArray *profiles = trg_prefs_get_profiles(p); + GList *profilesList = json_array_get_elements(profiles); + + GList *li; + JsonNode *node; + int i = 0; + + for (li = profilesList; li; li = g_list_next(li)) { + node = (JsonNode *) li->data; + if (profile == json_node_get_object(node)) { + json_array_remove_element(profiles, i); + break; + } + i++; + } + + g_list_free(profilesList); + + trg_prefs_profile_change_emit_signal(p); +} + +JsonObject *trg_prefs_get_profile(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + return priv->profile; +} + +JsonObject *trg_prefs_get_connection(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + return priv->connectionObj; +} + +JsonArray *trg_prefs_get_profiles(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + return json_object_get_array_member(priv->userObj, + TRG_PREFS_KEY_PROFILES); +} + +void +trg_prefs_set_double(TrgPrefs * p, const gchar * key, gdouble value, + int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_VALUE, + flags | TRG_PREFS_NEWNODE); + json_node_set_double(node, value); + trg_prefs_changed_emit_signal(p, key); +} + +void +trg_prefs_set_bool(TrgPrefs * p, const gchar * key, gboolean value, + int flags) +{ + JsonNode *node = trg_prefs_get_value(p, key, JSON_NODE_VALUE, + flags | TRG_PREFS_NEWNODE); + json_node_set_boolean(node, value); + trg_prefs_changed_emit_signal(p, key); +} + +gboolean trg_prefs_save(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + JsonGenerator *gen = json_generator_new(); + gchar *dirName; + gboolean success = TRUE; + gboolean isNew = TRUE; + + dirName = g_path_get_dirname(priv->file); + if (!g_file_test(dirName, G_FILE_TEST_IS_DIR)) { + success = g_mkdir_with_parents(dirName, TRG_PREFS_DEFAULT_DIR_MODE) + == 0; + } else if (g_file_test(priv->file, G_FILE_TEST_IS_REGULAR)) { + isNew = FALSE; + } + g_free(dirName); + + if (!success) { + g_error + ("Problem creating parent directory (permissions?) for: %s\n", + priv->file); + return success; + } + + g_object_set(G_OBJECT(gen), "pretty", TRUE, NULL); + json_generator_set_root(gen, priv->user); + + success = json_generator_to_file(gen, priv->file, NULL); + + if (!success) + g_error("Problem writing configuration file (permissions?) to: %s", + priv->file); + else if (isNew) + g_chmod(priv->file, 384); + + g_object_unref(gen); + + return success; +} + +JsonObject *trg_prefs_get_root(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + return priv->userObj; +} + +void trg_prefs_empty_init(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + JsonArray *profiles = json_array_new(); + + priv->user = json_node_new(JSON_NODE_OBJECT); + priv->userObj = json_object_new(); + json_node_take_object(priv->user, priv->userObj); + + priv->profile = trg_prefs_new_profile_object(); + + json_array_add_object_element(profiles, priv->profile); + json_object_set_array_member(priv->userObj, TRG_PREFS_KEY_PROFILES, + profiles); + + json_object_set_int_member(priv->userObj, TRG_PREFS_KEY_PROFILE_ID, 0); +} + +void trg_prefs_load(TrgPrefs * p) +{ + TrgPrefsPrivate *priv = p->priv; + JsonParser *parser = json_parser_new(); + JsonNode *root; + guint n_profiles; + JsonArray *profiles; + + gboolean parsed = json_parser_load_from_file(parser, priv->file, NULL); + + if (!parsed) { + trg_prefs_empty_init(p); + g_object_unref(parser); + return; + } + + root = json_parser_get_root(parser); + if (root) { + priv->user = json_node_copy(root); + priv->userObj = json_node_get_object(priv->user); + } + + g_object_unref(parser); + + if (!root) { + trg_prefs_empty_init(p); + return; + } + + if (!json_object_has_member(priv->userObj, TRG_PREFS_KEY_PROFILES)) { + profiles = json_array_new(); + json_object_set_array_member(priv->userObj, TRG_PREFS_KEY_PROFILES, + profiles); + } else { + profiles = json_object_get_array_member(priv->userObj, + TRG_PREFS_KEY_PROFILES); + } + + n_profiles = json_array_get_length(profiles); + + if (n_profiles < 1) { + priv->profile = trg_prefs_new_profile_object(); + json_array_add_object_element(profiles, priv->profile); + trg_prefs_set_int(p, TRG_PREFS_KEY_PROFILE_ID, 0, + TRG_PREFS_GLOBAL); + } else { + gint profile_id = trg_prefs_get_int(p, TRG_PREFS_KEY_PROFILE_ID, + TRG_PREFS_GLOBAL); + if (profile_id >= n_profiles) + trg_prefs_set_int(p, TRG_PREFS_KEY_PROFILE_ID, profile_id = 0, + TRG_PREFS_GLOBAL); + + priv->profile = + json_array_get_object_element(profiles, profile_id); + } +} + +guint trg_prefs_get_add_flags(TrgPrefs * p) +{ + guint flags = 0x00; + + if (trg_prefs_get_bool + (p, TRG_PREFS_KEY_START_PAUSED, TRG_PREFS_GLOBAL)) + flags |= TORRENT_ADD_FLAG_PAUSED; + + if (trg_prefs_get_bool + (p, TRG_PREFS_KEY_DELETE_LOCAL_TORRENT, TRG_PREFS_GLOBAL)) + flags |= TORRENT_ADD_FLAG_DELETE; + + return flags; +} -- cgit v1.2.3