pidgin/pidgin

Parents 2e00aa78cb1f
Children a3d48b662756
Implement PurplePrefsUiOps.(dis)connect_callback from instantbird

They were called observers in instantbird, but since that's a mozilla specific
term, I made it generic. Most of the code checks if connect_callback is defined
to call the functions that implement callback handling for the UI.

The main difference is that when the UI overrides the handling of the
preferences, find_pref() won't work, and the callbacks have to be stored
elsewhere.
--- a/libpurple/prefs.c Wed Jun 15 04:59:21 2016 -0300
+++ b/libpurple/prefs.c Thu Jun 16 02:10:34 2016 -0300
@@ -42,11 +42,13 @@
static PurplePrefsUiOps *prefs_ui_ops = NULL;
-struct pref_cb {
+struct _PurplePrefCallbackData {
PurplePrefCallback func;
gpointer data;
guint id;
void *handle;
+ void *ui_data;
+ char *name;
};
/* TODO: This should use PurpleValues? */
@@ -80,6 +82,7 @@
static GHashTable *prefs_hash = NULL;
static guint save_timer = 0;
static gboolean prefs_loaded = FALSE;
+static GSList *ui_callbacks = NULL;
#define PURPLE_PREFS_UI_OP_CALL(member, ...) \
{ \
@@ -823,16 +826,51 @@
struct purple_pref *cb_pref;
for(cb_pref = pref; cb_pref; cb_pref = cb_pref->parent) {
for(cbs = cb_pref->callbacks; cbs; cbs = cbs->next) {
- struct pref_cb *cb = cbs->data;
+ PurplePrefCallbackData *cb = cbs->data;
cb->func(name, pref->type, pref->value.generic, cb->data);
}
}
}
+static void
+do_ui_callbacks(const char *name)
+{
+ GSList *cbs;
+
+ purple_debug_misc("prefs", "trigger callback %s\n", name);
+
+ for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
+ PurplePrefCallbackData *cb = cbs->data;
+ const char *cb_name = cb->name;
+ size_t len = strlen(cb_name);
+ if (!strncmp(cb_name, name, len) &&
+ (name[len] == 0 || name[len] == '/' ||
+ (len && name[len - 1] == '/'))) {
+ /* This test should behave like this:
+ * name = /toto/tata
+ * cb_name = /toto/tata --> true
+ * cb_name = /toto/tatatiti --> false
+ * cb_name = / --> true
+ * cb_name = /toto --> true
+ * cb_name = /toto/ --> true
+ */
+ purple_prefs_trigger_callback_object(cbs->data);
+ }
+ }
+}
+
void
purple_prefs_trigger_callback(const char *name)
{
- struct purple_pref *pref = find_pref(name);
+ struct purple_pref *pref;
+ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
+
+ if (uiop && uiop->connect_callback) {
+ do_ui_callbacks(name);
+ return;
+ }
+
+ pref = find_pref(name);
if(!pref) {
purple_debug_error("prefs",
@@ -1355,31 +1393,103 @@
guint
purple_prefs_connect_callback(void *handle, const char *name, PurplePrefCallback func, gpointer data)
{
- struct purple_pref *pref;
- struct pref_cb *cb;
+ struct purple_pref *pref = NULL;
+ PurplePrefCallbackData *cb;
static guint cb_id = 0;
+ PurplePrefsUiOps *uiop = NULL;
g_return_val_if_fail(name != NULL, 0);
g_return_val_if_fail(func != NULL, 0);
- pref = find_pref(name);
- if (pref == NULL) {
- purple_debug_error("prefs", "purple_prefs_connect_callback: Unknown pref %s\n", name);
- return 0;
+ uiop = purple_prefs_get_ui_ops();
+
+ if (!(uiop && uiop->connect_callback)) {
+ pref = find_pref(name);
+ if (pref == NULL) {
+ purple_debug_error("prefs", "purple_prefs_connect_callback: Unknown pref %s\n", name);
+ return 0;
+ }
}
- cb = g_new0(struct pref_cb, 1);
+ cb = g_new0(PurplePrefCallbackData, 1);
cb->func = func;
cb->data = data;
cb->id = ++cb_id;
cb->handle = handle;
+ cb->name = g_strdup(name);
- pref->callbacks = g_slist_append(pref->callbacks, cb);
+ if (uiop && uiop->connect_callback) {
+ cb->ui_data = uiop->connect_callback(name, cb);
+
+ if (cb->ui_data == NULL) {
+ purple_debug_error("prefs", "purple_prefs_connect_callback: connect failed for %s\n", name);
+ g_free(cb->name);
+ g_free(cb);
+ return 0;
+ }
+
+ ui_callbacks = g_slist_append(ui_callbacks, cb);
+ } else {
+ pref->callbacks = g_slist_append(pref->callbacks, cb);
+ }
return cb->id;
}
+static void
+purple_prefs_trigger_ui_callback_object(PurplePrefCallbackData *cb)
+{
+ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
+ gconstpointer value = NULL;
+ PurplePrefType type = PURPLE_PREF_NONE;
+
+ type = uiop->get_type(cb->name);
+
+ switch (type) {
+ case PURPLE_PREF_INT:
+ if (uiop->get_int) {
+ value = GINT_TO_POINTER(uiop->get_int(cb->name));
+ }
+ break;
+ case PURPLE_PREF_BOOLEAN:
+ if (uiop->get_bool) {
+ value = GINT_TO_POINTER(uiop->get_bool(cb->name));
+ }
+ break;
+ case PURPLE_PREF_STRING:
+ case PURPLE_PREF_PATH:
+ if (uiop->get_string) {
+ value = uiop->get_string(cb->name);
+ }
+ break;
+ case PURPLE_PREF_STRING_LIST:
+ case PURPLE_PREF_PATH_LIST:
+ if (uiop->get_string_list) {
+ value = uiop->get_string_list(cb->name);
+ }
+ break;
+ case PURPLE_PREF_NONE:
+ break;
+ default:
+ purple_debug_error("prefs", "Unexpected type = %i\n", type);
+ }
+
+ cb->func(cb->name, type, value, cb->data);
+}
+
+void
+purple_prefs_trigger_callback_object(PurplePrefCallbackData *cb)
+{
+ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
+
+ if (uiop && uiop->connect_callback && uiop->get_type) {
+ purple_prefs_trigger_ui_callback_object(cb);
+ } else {
+ purple_prefs_trigger_callback(cb->name);
+ }
+}
+
static gboolean
disco_callback_helper(struct purple_pref *pref, guint callback_id)
{
@@ -1390,9 +1500,10 @@
return FALSE;
for(cbs = pref->callbacks; cbs; cbs = cbs->next) {
- struct pref_cb *cb = cbs->data;
+ PurplePrefCallbackData *cb = cbs->data;
if(cb->id == callback_id) {
pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
+ g_free(cb->name);
g_free(cb);
return TRUE;
}
@@ -1406,10 +1517,35 @@
return FALSE;
}
+static void
+disco_ui_callback_helper(guint callback_id)
+{
+ GSList *cbs;
+ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
+
+ for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
+ PurplePrefCallbackData *cb = cbs->data;
+ if (cb->id == callback_id) {
+ uiop->disconnect_callback(cb->name, cb->ui_data);
+
+ ui_callbacks = g_slist_delete_link(ui_callbacks, cbs);
+ g_free(cb->name);
+ g_free(cb);
+ return;
+ }
+ }
+}
+
void
purple_prefs_disconnect_callback(guint callback_id)
{
- disco_callback_helper(&prefs, callback_id);
+ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
+
+ if (uiop && uiop->disconnect_callback) {
+ disco_ui_callback_helper(callback_id);
+ } else {
+ disco_callback_helper(&prefs, callback_id);
+ }
}
static void
@@ -1423,9 +1559,10 @@
cbs = pref->callbacks;
while (cbs != NULL) {
- struct pref_cb *cb = cbs->data;
+ PurplePrefCallbackData *cb = cbs->data;
if(cb->handle == handle) {
pref->callbacks = g_slist_delete_link(pref->callbacks, cbs);
+ g_free(cb->name);
g_free(cb);
cbs = pref->callbacks;
} else
@@ -1436,12 +1573,41 @@
disco_callback_helper_handle(child, handle);
}
+static void
+disco_ui_callback_helper_handle(void *handle)
+{
+ GSList *cbs;
+ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
+
+ cbs = ui_callbacks;
+ for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
+ PurplePrefCallbackData *cb = cbs->data;
+ if (cb->handle != handle) {
+ cbs = cbs->next;
+ continue;
+ }
+
+ uiop->disconnect_callback(cb->name, cb->ui_data);
+
+ ui_callbacks = g_slist_delete_link(ui_callbacks, cbs);
+ g_free(cb->name);
+ g_free(cb);
+ cbs = ui_callbacks;
+ }
+}
+
void
purple_prefs_disconnect_by_handle(void *handle)
{
+ PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
+
g_return_if_fail(handle != NULL);
- disco_callback_helper_handle(&prefs, handle);
+ if (uiop && uiop->disconnect_callback) {
+ disco_ui_callback_helper_handle(handle);
+ } else {
+ disco_callback_helper_handle(&prefs, handle);
+ }
}
GList *
--- a/libpurple/prefs.h Wed Jun 15 04:59:21 2016 -0300
+++ b/libpurple/prefs.h Thu Jun 16 02:10:34 2016 -0300
@@ -62,6 +62,11 @@
typedef void (*PurplePrefCallback) (const char *name, PurplePrefType type,
gconstpointer val, gpointer data);
+/**
+ * Opaque type to carry callback information
+ */
+typedef struct _PurplePrefCallbackData PurplePrefCallbackData;
+
/** @copydoc _PurplePrefsUiOps */
typedef struct _PurplePrefsUiOps PurplePrefsUiOps;
@@ -100,8 +105,8 @@
void (*save)(void);
void (*schedule_save)(void);
- void *(*add_observer)(const char *name, gpointer data);
- void (*remove_observer)(const char *name, void *observer);
+ void *(*connect_callback)(const char *name, PurplePrefCallbackData *data);
+ void (*disconnect_callback)(const char *name, void *ui_data);
void (*_purple_reserved1)(void);
void (*_purple_reserved2)(void);
@@ -423,6 +428,13 @@
void purple_prefs_trigger_callback(const char *name);
/**
+ * Trigger callbacks as if the pref changed, taking a #PurplePrefCallbackData
+ * instead of a name
+ */
+void purple_prefs_trigger_callback_object(PurplePrefCallbackData *data);
+
+
+/**
* Read preferences
*/
gboolean purple_prefs_load(void);