phonemgr r303 - in trunk: . cut-n-paste cut-n-paste/e-contact-entry cut-n-paste/gconf-bridge src
- From: baptistem svn gnome org
- To: svn-commits-list gnome org
- Subject: phonemgr r303 - in trunk: . cut-n-paste cut-n-paste/e-contact-entry cut-n-paste/gconf-bridge src
- Date: Mon, 16 Jun 2008 20:28:51 +0000 (UTC)
Author: baptistem
Date: Mon Jun 16 20:28:51 2008
New Revision: 303
URL: http://svn.gnome.org/viewvc/phonemgr?rev=303&view=rev
Log:
* Makefile.am,
configure.in,
cut-n-paste/Makefile.am,
cut-n-paste/update-from-egg.sh,
cut-n-paste/e-contact-entry/e-contact-entry.c,
cut-n-paste/e-contact-entry/e-contact-entry.h,
cut-n-paste/e-contact-entry/econtactentry-marshal.list,
cut-n-paste/e-contact-entry/Makefile.am,
cut-n-paste/gconf-bridge/gconf-bridge.c,
cut-n-paste/gconf-bridge/gconf-bridge.h,
cut-n-paste/gconf-bridge/Makefile.am,
src/e-contact-entry.c,
src/e-contact-entry.h,
src/econtactentry-marshal.list,
src/gconf-bridge.c,
src/gconf-bridge.h,
src/Makefile.am: move cut'n pasted code to cut-n-paste/ to prepare
gnome-bluetooth dependency removal (step 1 of #455838)
Added:
trunk/cut-n-paste/
trunk/cut-n-paste/Makefile.am
trunk/cut-n-paste/e-contact-entry/
trunk/cut-n-paste/e-contact-entry/Makefile.am
trunk/cut-n-paste/e-contact-entry/e-contact-entry.c
trunk/cut-n-paste/e-contact-entry/e-contact-entry.h
trunk/cut-n-paste/e-contact-entry/econtactentry-marshal.list
trunk/cut-n-paste/gconf-bridge/
trunk/cut-n-paste/gconf-bridge/Makefile.am
trunk/cut-n-paste/gconf-bridge/gconf-bridge.c
trunk/cut-n-paste/gconf-bridge/gconf-bridge.h
trunk/cut-n-paste/update-from-egg.sh (contents, props changed)
Removed:
trunk/src/e-contact-entry.c
trunk/src/e-contact-entry.h
trunk/src/econtactentry-marshal.list
trunk/src/gconf-bridge.c
trunk/src/gconf-bridge.h
trunk/src/update-from-egg.sh
Modified:
trunk/ChangeLog
trunk/Makefile.am
trunk/configure.in
trunk/src/Makefile.am
Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am (original)
+++ trunk/Makefile.am Mon Jun 16 20:28:51 2008
@@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in
-SUBDIRS = po libgsm src telepathy data
+SUBDIRS = po libgsm cut-n-paste src telepathy data
EXTRA_DIST = \
autogen.sh \
Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in (original)
+++ trunk/configure.in Mon Jun 16 20:28:51 2008
@@ -162,6 +162,9 @@
data/icons/24x24/status/Makefile
data/icons/24x24/Makefile
libgsm/Makefile
+cut-n-paste/Makefile
+cut-n-paste/e-contact-entry/Makefile
+cut-n-paste/gconf-bridge/Makefile
src/Makefile
telepathy/Makefile
po/Makefile.in
Added: trunk/cut-n-paste/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/Makefile.am Mon Jun 16 20:28:51 2008
@@ -0,0 +1 @@
+SUBDIRS = e-contact-entry gconf-bridge
Added: trunk/cut-n-paste/e-contact-entry/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/e-contact-entry/Makefile.am Mon Jun 16 20:28:51 2008
@@ -0,0 +1,18 @@
+AM_CPPFLAGS = \
+ $(PHONEMGR_CFLAGS)
+
+noinst_LTLIBRARIES = libecontact-entry.la
+
+libecontact_entry_la_SOURCES = \
+ e-contact-entry.c \
+ e-contact-entry.h \
+ econtactentry-marshal.c
+
+MARSHALFILES = econtactentry-marshal.c econtactentry-marshal.h
+BUILT_SOURCES = $(MARSHALFILES)
+econtactentry-marshal.c: econtactentry-marshal.h
+ ( $(GLIB_GENMARSHAL) --prefix=econtactentry_marshal $(srcdir)/econtactentry-marshal.list --header --body > econtactentry-marshal.c )
+econtactentry-marshal.h: econtactentry-marshal.list
+ ( $(GLIB_GENMARSHAL) --prefix=econtactentry_marshal $(srcdir)/econtactentry-marshal.list --header > econtactentry-marshal.h )
+
+CLEANFILES = $(BUILT_SOURCES)
Added: trunk/cut-n-paste/e-contact-entry/e-contact-entry.c
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/e-contact-entry/e-contact-entry.c Mon Jun 16 20:28:51 2008
@@ -0,0 +1,933 @@
+/*
+ * Copyright (C) 2004 Ross Burton <ross burtonini com
+ *
+ * e-contact-entry.c
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: Ross Burton <ross burtonini com>
+ */
+
+/*
+ * TODO:
+ *
+ * Either a clear boolean property, or provide a _clear() method?
+ *
+ * set_sources() should keep a ref to the sources and connect signal handlers
+ * to manage the list. Or just expose a GList* of ESource*
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtkentry.h>
+#include <gtk/gtkentrycompletion.h>
+#include <gtk/gtkcelllayout.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkcellrendererpixbuf.h>
+
+#include <libedataserver/e-source.h>
+#include <libebook/e-book.h>
+#include <libebook/e-book-view.h>
+#include <libebook/e-contact.h>
+
+#include "e-contact-entry.h"
+#include "econtactentry-marshal.h"
+
+/* Signals */
+enum {
+ CONTACT_SELECTED, /* Signal argument is the contact. ref it if you want to keep it */
+ ERROR,
+ STATE_CHANGE,
+ LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL] = { 0 };
+
+/* Properties */
+enum {
+ PROP_0, /* TODO: why? */
+ PROP_SOURCE_LIST,
+ PROP_COMPLETE_LENGTH,
+};
+
+/*
+ * TODO: store ESource* in EntryLookup, remove sources.
+ */
+
+struct EContactEntryPriv {
+ GtkEntryCompletion *completion;
+ GtkListStore *store;
+ ESourceList *source_list;
+ /* A list of EntryLookup structs we are searching */
+ GList *lookup_entries;
+ /* Number of characters to start searching at */
+ int lookup_length;
+ /* A null-terminated array of fields to complete on,
+ * n_search_fields includes the terminating NULL */
+ EContactField *search_fields;
+ int n_search_fields;
+ /* Display callback */
+ EContactEntryDisplayFunc display_func;
+ gpointer display_data;
+ GDestroyNotify display_destroy;
+};
+
+/**
+ * Struct containing details of the sources we are searching.
+ */
+typedef struct _EntryLookup {
+ EContactEntry *entry;
+ gboolean open;
+ EBookStatus status;
+ EBook *book;
+ EBookView *bookview;
+} EntryLookup;
+
+/**
+ * List store columns.
+ */
+enum {
+ COL_NAME,
+ COL_IDENTIFIER,
+ COL_UID,
+ COL_PHOTO,
+ COL_LOOKUP,
+ COL_TOTAL
+};
+
+G_DEFINE_TYPE(EContactEntry, e_contact_entry, GTK_TYPE_ENTRY);
+
+static void lookup_entry_free (EntryLookup *lookup);
+static EBookQuery* create_query (EContactEntry *entry, const char* s);
+static guint entry_height (GtkWidget *widget);
+static const char* stringify_ebook_error (const EBookStatus status);
+static void e_contact_entry_item_free (EContactEntyItem *item);
+
+/**
+ * The entry was activated. Take the first contact found and signal the user.
+ */
+static void
+entry_activate_cb (EContactEntry *entry, gpointer user_data)
+{
+ GtkTreeIter iter;
+
+ g_return_if_fail (E_IS_CONTACT_ENTRY (entry));
+ if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (entry->priv->store), &iter)) {
+ /*
+ * Two possible situations:
+ *
+ * 1) the search is still in progress
+ * 2) the search doesn't match any contacts
+ *
+ * For (2) we beep. For (1) the user suffers for now, and TODO: add a
+ * searching boolean.
+ */
+ gdk_beep ();
+ } else {
+ char *uid, *identifier;
+ EntryLookup *lookup;
+ EContact *contact;
+ GError *error = NULL;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (entry->priv->store), &iter, COL_UID, &uid, COL_LOOKUP, &lookup, COL_IDENTIFIER, &identifier, -1);
+ g_return_if_fail (lookup != NULL);
+
+ gtk_entry_set_text (GTK_ENTRY (entry), "");
+
+ if (!e_book_get_contact (lookup->book, uid, &contact, &error)) {
+ char* message;
+ message = g_strdup_printf(_("Cannot get contact: %s"), error->message);
+ g_signal_emit (entry, signals[ERROR], 0, message);
+ g_free (message);
+ g_error_free (error);
+ } else {
+ g_signal_emit (G_OBJECT (entry), signals[CONTACT_SELECTED], 0, contact, identifier);
+ g_object_unref (contact);
+ }
+ g_free (uid);
+ g_free (identifier);
+
+ gtk_list_store_clear (entry->priv->store);
+ }
+}
+
+/**
+ * A contact was selected in the completion drop-down, so send a signal.
+ */
+static gboolean
+completion_match_selected_cb (GtkEntryCompletion *completion, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
+{
+ EContactEntry *entry;
+ EntryLookup *lookup;
+ char *uid, *identifier;
+ EContact *contact = NULL;
+ GError *error = NULL;
+
+ g_return_val_if_fail (user_data != NULL, TRUE);
+ entry = (EContactEntry*)user_data;
+
+ gtk_tree_model_get (model, iter, COL_UID, &uid, COL_LOOKUP, &lookup, COL_IDENTIFIER, &identifier, -1);
+ if (!e_book_get_contact (lookup->book, uid, &contact, &error)) {
+ char *message;
+ message = g_strdup_printf (_("Could not find contact: %s"), error->message);
+ g_signal_emit (entry, signals[ERROR], 0, message);
+ g_free (message);
+ return FALSE;
+ }
+ gtk_entry_set_text (GTK_ENTRY (entry), "");
+ g_signal_emit (G_OBJECT (entry), signals[CONTACT_SELECTED], 0, contact, identifier);
+ g_object_unref (contact);
+ g_free (uid);
+ g_free (identifier);
+
+ gtk_list_store_clear (entry->priv->store);
+ return TRUE;
+}
+
+static gboolean
+completion_match_cb (GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data)
+{
+ /* Assumption: all entries in the model are valid candiates */
+ /* TODO: do we really need to do all this? */
+ char *cell;
+ gtk_tree_model_get (gtk_entry_completion_get_model (completion), iter, 0, &cell, -1);
+ if (cell == NULL) {
+ /* We get NULL cells if we've appended to the store without setting. */
+ return FALSE;
+ } else {
+ /* Is there anything sane to do here? */
+ g_free (cell);
+ return TRUE;
+ }
+}
+
+static GList *
+e_contact_entry_display_func (EContact *contact)
+{
+ GList *items, *emails, *l;
+ EContactEntyItem *item;
+
+ items = NULL;
+ emails = e_contact_get (contact, E_CONTACT_EMAIL);
+ for (l = emails; l != NULL; l = l->next) {
+ item = g_new0 (EContactEntyItem, 1);
+ item->identifier = item->identifier = g_strdup (l->data);
+ item->display_string = g_strdup_printf ("%s <%s>", (char*)e_contact_get_const (contact, E_CONTACT_NAME_OR_ORG), item->identifier);
+
+ items = g_list_prepend (items, item);
+ }
+
+ return g_list_reverse (items);
+}
+
+/* This is the maximum number of entries that GTK+ will show */
+#define MAX_ENTRIES 15
+/**
+ * Callback from the EBookView that more contacts matching the query have been found. Add these to
+ * the model if we still want more contacts, or stop the view.
+ */
+static void
+view_contacts_added_cb (EBook *book, GList *contacts, gpointer user_data)
+{
+ EntryLookup *lookup;
+ guint max_height;
+ int i;
+
+ g_return_if_fail (user_data != NULL);
+ g_return_if_fail (contacts != NULL);
+ lookup = (EntryLookup*)user_data;
+
+ max_height = entry_height (GTK_WIDGET (lookup->entry));
+
+ for (i = 0; contacts != NULL && i < MAX_ENTRIES; contacts = g_list_next (contacts)) {
+ GtkTreeIter iter;
+ EContact *contact;
+ EContactPhoto *photo;
+ GdkPixbuf *pixbuf = NULL;
+ GList *entries, *e;
+
+ entries = NULL;
+ contact = E_CONTACT (contacts->data);
+
+ if (lookup->entry->priv->display_func) {
+ entries = lookup->entry->priv->display_func (contact, lookup->entry->priv->display_data);
+ } else {
+ entries = e_contact_entry_display_func (contact);
+ }
+
+ /* Don't add the contact to the list if we don't have a string */
+ if (entries == NULL)
+ return;
+
+ photo = e_contact_get (contact, E_CONTACT_PHOTO);
+#ifndef HAVE_ECONTACTPHOTOTYPE
+ if (photo) {
+#else
+ if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
+#endif
+ GdkPixbufLoader *loader;
+
+ loader = gdk_pixbuf_loader_new ();
+
+#ifndef HAVE_ECONTACTPHOTOTYPE
+ if (gdk_pixbuf_loader_write (loader, (guchar *)photo->data,
+ photo->length, NULL))
+#else
+ if (gdk_pixbuf_loader_write (loader, (guchar *)photo->data.inlined.data,
+ photo->data.inlined.length, NULL))
+#endif
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+
+ if (pixbuf) {
+ GdkPixbuf *tmp;
+ gint width = gdk_pixbuf_get_width (pixbuf);
+ gint height = gdk_pixbuf_get_height (pixbuf);
+ double scale = 1.0;
+
+ if (height > width) {
+ scale = max_height / (double) height;
+ } else {
+ scale = max_height / (double) width;
+ }
+
+ if (scale < 1.0) {
+ tmp = gdk_pixbuf_scale_simple (pixbuf, width * scale, height * scale, GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+ pixbuf = tmp;
+ }
+ }
+ }
+ if (photo)
+ e_contact_photo_free (photo);
+
+ for (e = entries; e; e = e->next) {
+ EContactEntyItem *item = e->data;
+
+ gtk_list_store_append (lookup->entry->priv->store, &iter);
+ /* At this point the matcher callback gets called */
+ gtk_list_store_set (lookup->entry->priv->store, &iter,
+ COL_NAME, item->display_string,
+ COL_IDENTIFIER, item->identifier,
+ COL_UID, e_contact_get_const (contact, E_CONTACT_UID),
+ COL_PHOTO, pixbuf,
+ COL_LOOKUP, lookup,
+ -1);
+ e_contact_entry_item_free (item);
+ }
+ g_list_free (entries);
+ if (pixbuf) g_object_unref (pixbuf);
+ }
+}
+
+/**
+ * The query on the EBookView has completed.
+ */
+static void
+view_completed_cb (EBookView *book_view, EBookViewStatus status, gpointer user_data)
+{
+ EntryLookup *lookup;
+ g_return_if_fail (user_data != NULL);
+ /* TODO: handle status != OK */
+ g_return_if_fail (status == E_BOOK_ERROR_OK);
+ g_return_if_fail (book_view != NULL);
+
+ lookup = (EntryLookup*)user_data;
+ g_object_unref (lookup->bookview);
+}
+
+/**
+ * The EBookView to lookup the completions with has been created.
+ */
+static void
+bookview_cb (EBook *book, EBookStatus status, EBookView *book_view, gpointer closure)
+{
+ EntryLookup *lookup;
+ /* TODO: handle status != OK */
+ g_return_if_fail (status == E_BOOK_ERROR_OK);
+ g_return_if_fail (closure != NULL);
+
+ lookup = (EntryLookup*)closure;
+
+ g_object_ref (book_view);
+ /* This shouldn't happen of course */
+ if (lookup->bookview) {
+ e_book_view_stop (lookup->bookview);
+ g_object_unref (lookup->bookview);
+ }
+ lookup->bookview = book_view;
+ g_object_add_weak_pointer ((GObject*)book_view, (gpointer*)&lookup->bookview);
+
+ g_signal_connect (book_view, "contacts_added", (GCallback)view_contacts_added_cb, lookup);
+ g_signal_connect (book_view, "sequence_complete", (GCallback)view_completed_cb, lookup);
+
+ e_book_view_start (book_view);
+}
+
+static void
+entry_changed_cb (GtkEditable *editable, gpointer user_data)
+{
+ EContactEntry *entry;
+ entry = E_CONTACT_ENTRY (editable);
+
+ if (GTK_ENTRY (editable)->text_length >= entry->priv->lookup_length) {
+ GList *l;
+ EBookQuery *query;
+
+ /* TODO: I appear to do this to stop duplicates, but its a bit of a hack */
+ for (l = entry->priv->lookup_entries; l != NULL; l = l->next) {
+ EntryLookup *lookup;
+ lookup = (EntryLookup*)l->data;
+ if (lookup->bookview) {
+ e_book_view_stop (lookup->bookview);
+ g_object_unref (lookup->bookview);
+ }
+ }
+
+ gtk_list_store_clear (entry->priv->store);
+
+ query = create_query (entry, gtk_editable_get_chars (editable, 0, -1));
+ for (l = entry->priv->lookup_entries; l != NULL; l = l->next) {
+ EntryLookup *lookup;
+ lookup = (EntryLookup*)l->data;
+
+ /* If the book isn't open yet, skip this source */
+ if (!lookup->open)
+ continue;
+
+ if (e_book_async_get_book_view (lookup->book, query, NULL, 11, (EBookBookViewCallback)bookview_cb, lookup) != 0) {
+ g_signal_emit (entry, signals[ERROR], 0, _("Cannot create searchable view."));
+ }
+ }
+ e_book_query_unref (query);
+ }
+}
+
+static void
+book_opened_cb (EBook *book, EBookStatus status, gpointer data)
+{
+ EntryLookup *lookup;
+
+ g_return_if_fail (book != NULL);
+ g_return_if_fail (data != NULL);
+
+ lookup = (EntryLookup*)data;
+
+ /* Don't error out if we're not the last one to open */
+ lookup->status = status;
+ if (status != E_BOOK_ERROR_OK) {
+ GList *l;
+
+ for (l = lookup->entry->priv->lookup_entries; l != NULL; l = l->next) {
+ EntryLookup *e = l->data;
+ /* Not opened yet is ->open false && ->status not an error */
+ if (e->open != FALSE || e->status == E_BOOK_ERROR_OK) {
+ /* Don't error yet */
+ return;
+ }
+ }
+
+ g_signal_emit (lookup->entry, signals[STATE_CHANGE], 0, FALSE);
+ g_signal_emit (lookup->entry, signals[ERROR], 0, stringify_ebook_error (status));
+ return;
+ }
+ lookup->open = TRUE;
+ g_signal_emit (lookup->entry, signals[STATE_CHANGE], 0, TRUE);
+}
+
+
+/*
+ *
+ * Accessors to the fields
+ *
+ */
+
+void
+e_contact_entry_set_source_list (EContactEntry *entry,
+ ESourceList *source_list)
+{
+ GError *error = NULL;
+ GSList *list, *l;
+
+ g_return_if_fail (E_IS_CONTACT_ENTRY (entry));
+
+ /* Release the old sources */
+ if (entry->priv->lookup_entries) {
+ g_list_foreach (entry->priv->lookup_entries, (GFunc)lookup_entry_free, NULL);
+ g_list_free (entry->priv->lookup_entries);
+ }
+ if (entry->priv->source_list) {
+ g_object_unref (entry->priv->source_list);
+ }
+
+ /* If we have no new sources, disable and return here */
+ if (source_list == NULL) {
+ g_signal_emit (entry, signals[STATE_CHANGE], 0, FALSE);
+ entry->priv->source_list = NULL;
+ entry->priv->lookup_entries = NULL;
+ return;
+ }
+
+ entry->priv->source_list = source_list;
+ /* So that the list isn't going away underneath us */
+ g_object_ref (entry->priv->source_list);
+
+ /* That gets us a list of ESourceGroup */
+ list = e_source_list_peek_groups (source_list);
+ entry->priv->lookup_entries = NULL;
+
+ for (l = list; l != NULL; l = l->next) {
+ ESourceGroup *group = l->data;
+ GSList *sources = NULL, *m;
+ /* That should give us a list of ESource */
+ sources = e_source_group_peek_sources (group);
+ for (m = sources; m != NULL; m = m->next) {
+ ESource *source = m->data;
+ const char *p;
+ ESource *s = e_source_copy (source);
+ EntryLookup *lookup;
+ char *uri;
+
+ uri = g_strdup_printf("%s/%s", e_source_group_peek_base_uri (group), e_source_peek_relative_uri (source));
+ e_source_set_absolute_uri (s, uri);
+ g_free (uri);
+
+ /* Now add those to the lookup entries list */
+ lookup = g_new0 (EntryLookup, 1);
+ lookup->entry = entry;
+ lookup->status = E_BOOK_ERROR_OK;
+ lookup->open = FALSE;
+
+ if ((lookup->book = e_book_new (s, &error)) == NULL) {
+ /* TODO handle this better, fire the error signal I guess */
+ g_warning (error->message);
+ g_error_free (error);
+ g_free (lookup);
+ } else {
+ entry->priv->lookup_entries = g_list_append (entry->priv->lookup_entries, lookup);
+ e_book_async_open(lookup->book, TRUE, (EBookCallback)book_opened_cb, lookup);
+ }
+
+ g_object_unref (s);
+ }
+ }
+
+ if (entry->priv->lookup_entries == NULL)
+ g_signal_emit (entry, signals[STATE_CHANGE], 0, FALSE);
+}
+
+ESourceList *
+e_contact_entry_get_source_list (EContactEntry *entry)
+{
+ g_return_val_if_fail (E_IS_CONTACT_ENTRY (entry), NULL);
+
+ return entry->priv->source_list;
+}
+
+void
+e_contact_entry_set_complete_length (EContactEntry *entry, int length)
+{
+ g_return_if_fail (E_IS_CONTACT_ENTRY (entry));
+ g_return_if_fail (length >= 1);
+
+ entry->priv->lookup_length = length;
+ gtk_entry_completion_set_minimum_key_length (entry->priv->completion, entry->priv->lookup_length);
+}
+
+int
+e_contact_entry_get_complete_length (EContactEntry *entry)
+{
+ g_return_val_if_fail (E_IS_CONTACT_ENTRY (entry), 3); /* TODO: from paramspec? */
+
+ return entry->priv->lookup_length;
+}
+
+void
+e_contact_entry_set_display_func (EContactEntry *entry, EContactEntryDisplayFunc func, gpointer func_data, GDestroyNotify destroy)
+{
+ g_return_if_fail (E_IS_CONTACT_ENTRY (entry));
+
+ if (entry->priv->display_destroy) {
+ entry->priv->display_destroy (entry->priv->display_func);
+ }
+
+ entry->priv->display_func = func;
+ entry->priv->display_data = func_data;
+ entry->priv->display_destroy = destroy;
+}
+
+void
+e_contact_entry_set_search_fields (EContactEntry *entry, const EContactField *fields)
+{
+ int i;
+
+ g_free (entry->priv->search_fields);
+ i = 0;
+ while (fields[i] != 0) {
+ i++;
+ }
+
+ entry->priv->search_fields = g_new0 (EContactField, i + 1);
+ memcpy (entry->priv->search_fields, fields, sizeof (EContactField) * (i + 1));
+ entry->priv->n_search_fields = i + 1;
+}
+
+/*
+ *
+ * GObject functions
+ *
+ */
+
+static void
+e_contact_entry_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ EContactEntry *entry;
+
+ g_return_if_fail (E_IS_CONTACT_ENTRY (object));
+ entry = E_CONTACT_ENTRY (object);
+
+ switch (property_id) {
+ case PROP_SOURCE_LIST:
+ e_contact_entry_set_source_list (entry, g_value_get_object (value));
+ break;
+ case PROP_COMPLETE_LENGTH:
+ e_contact_entry_set_complete_length (entry, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+e_contact_entry_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ EContactEntry *entry;
+ g_return_if_fail (E_IS_CONTACT_ENTRY (object));
+ entry = E_CONTACT_ENTRY (object);
+
+ switch (property_id) {
+ case PROP_SOURCE_LIST:
+ g_value_set_object (value, e_contact_entry_get_source_list (entry));
+ break;
+ case PROP_COMPLETE_LENGTH:
+ g_value_set_int (value, e_contact_entry_get_complete_length (entry));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+e_contact_entry_finalize (GObject *object)
+{
+ GList *l;
+ EContactEntry *entry = (EContactEntry *)object;
+ if (entry->priv) {
+ for (l = entry->priv->lookup_entries; l != NULL; l = g_list_next (l)) {
+ lookup_entry_free (l->data);
+ }
+ g_free (entry->priv->search_fields);
+ g_list_free (entry->priv->lookup_entries);
+ g_object_unref (entry->priv->completion);
+ g_object_unref (entry->priv->store);
+ g_object_unref (entry->priv->source_list);
+
+ if (entry->priv->display_destroy) {
+ entry->priv->display_destroy (entry->priv->display_func);
+ }
+ g_free (entry->priv);
+ }
+ G_OBJECT_CLASS (e_contact_entry_parent_class)->finalize (object);
+}
+
+static void
+reset_search_fields (EContactEntry *entry)
+{
+ EContactField fields[] = { E_CONTACT_FULL_NAME, E_CONTACT_EMAIL, E_CONTACT_NICKNAME, E_CONTACT_ORG, 0 };
+
+ g_free (entry->priv->search_fields);
+ entry->priv->search_fields = g_new0 (EContactField, G_N_ELEMENTS (fields));
+ memcpy (entry->priv->search_fields, fields, sizeof (fields));
+ entry->priv->n_search_fields = G_N_ELEMENTS (fields);
+}
+
+static void
+e_contact_entry_init (EContactEntry *entry)
+{
+ GtkCellRenderer *renderer;
+
+ entry->priv = g_new0 (EContactEntryPriv, 1);
+
+ g_signal_connect (entry, "activate", G_CALLBACK (entry_activate_cb), NULL);
+ g_signal_connect (entry, "changed", G_CALLBACK (entry_changed_cb), NULL);
+
+ entry->priv->store = gtk_list_store_new (COL_TOTAL, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER);
+
+ entry->priv->search_fields = NULL;
+ reset_search_fields (entry);
+
+ entry->priv->completion = gtk_entry_completion_new ();
+ gtk_entry_completion_set_popup_set_width (entry->priv->completion, FALSE);
+ gtk_entry_completion_set_model (entry->priv->completion, GTK_TREE_MODEL (entry->priv->store));
+ gtk_entry_completion_set_match_func (entry->priv->completion, (GtkEntryCompletionMatchFunc)completion_match_cb, NULL, NULL);
+ g_signal_connect (entry->priv->completion, "match-selected", G_CALLBACK (completion_match_selected_cb), entry);
+ e_contact_entry_set_complete_length (entry, G_PARAM_SPEC_INT (g_object_class_find_property (G_OBJECT_GET_CLASS(entry), "complete-length"))->default_value);
+ gtk_entry_set_completion (GTK_ENTRY (entry), entry->priv->completion);
+
+ /* Photo */
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry->priv->completion), renderer, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (entry->priv->completion), renderer, "pixbuf", COL_PHOTO);
+
+ /* Name */
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry->priv->completion), renderer, TRUE);
+ gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (entry->priv->completion), renderer, "text", COL_NAME);
+
+ entry->priv->lookup_entries = NULL;
+
+ entry->priv->display_func = entry->priv->display_data = entry->priv->display_destroy = NULL;
+}
+
+static void
+e_contact_entry_class_init (EContactEntryClass *klass)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ /* GObject */
+ object_class->set_property = e_contact_entry_set_property;
+ object_class->get_property = e_contact_entry_get_property;
+ object_class->finalize = e_contact_entry_finalize;
+
+ /* Properties */
+ g_object_class_install_property (object_class, PROP_SOURCE_LIST,
+ g_param_spec_object ("source-list", "Source List", "The source list to search for contacts.",
+ E_TYPE_SOURCE_LIST, G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_COMPLETE_LENGTH,
+ g_param_spec_int ("complete-length", "Complete length", "Number of characters to start a search on.",
+ 2, 99, 3, G_PARAM_READWRITE));
+
+ /* Signals */
+ signals[CONTACT_SELECTED] = g_signal_new ("contact-selected",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EContactEntryClass, contact_selected),
+ NULL, NULL,
+ econtactentry_marshal_VOID__OBJECT_STRING,
+ G_TYPE_NONE, 2, E_TYPE_CONTACT, G_TYPE_STRING);
+
+ signals[ERROR] = g_signal_new ("error",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EContactEntryClass, error),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+ signals[STATE_CHANGE] = g_signal_new ("state-change",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EContactEntryClass, state_change),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+GtkWidget *
+e_contact_entry_new (void)
+{
+ return g_object_new (e_contact_entry_get_type (), NULL);
+}
+
+
+/*
+ *
+ * Miscellaneous utility functions.
+ *
+ */
+
+static void
+lookup_entry_free (EntryLookup *lookup)
+{
+ /* We didn't take a reference for -> entry so don't unref it here. Yet. */
+ g_return_if_fail (lookup != NULL);
+ /* TODO: unref entry if we ref it anywhere */
+ if (lookup->bookview) {
+ g_warning("EBookView still around");
+ g_object_unref (lookup->bookview);
+ }
+ if (lookup->book) {
+ g_object_unref (lookup->book);
+ } else {
+ g_warning ("EntryLookup object with no book member");
+ }
+ g_free (lookup);
+}
+
+/**
+ * Split a string of tokens separated by whitespace into an array of tokens.
+ */
+static GArray *
+split_query_string (const gchar *str)
+{
+ GArray *parts = g_array_sized_new (FALSE, FALSE, sizeof (char *), 2);
+ PangoLogAttr *attrs;
+ guint str_len = strlen (str), word_start = 0, i;
+
+ attrs = g_new0 (PangoLogAttr, str_len + 1);
+ /* TODO: do we need to specify a particular language or is NULL ok? */
+ pango_get_log_attrs (str, -1, -1, NULL, attrs, str_len + 1);
+
+ for (i = 0; i < str_len + 1; i++) {
+ char *start_word, *end_word, *word;
+ if (attrs[i].is_word_end) {
+ start_word = g_utf8_offset_to_pointer (str, word_start);
+ end_word = g_utf8_offset_to_pointer (str, i);
+ word = g_strndup (start_word, end_word - start_word);
+ g_array_append_val (parts, word);
+ }
+ if (attrs[i].is_word_start) {
+ word_start = i;
+ }
+ }
+ g_free (attrs);
+ return parts;
+}
+
+/**
+ * Create a query which looks for the specified string in a contact's full name, email addresses and
+ * nick name.
+ */
+static EBookQuery*
+create_query (EContactEntry *entry, const char* s)
+{
+ EBookQuery *query;
+ GArray *parts = split_query_string (s);
+ /* TODO: ORG doesn't appear to work */
+ EBookQuery ***field_queries;
+ EBookQuery **q;
+ guint j;
+ int i;
+
+ q = g_new0 (EBookQuery *, entry->priv->n_search_fields - 1);
+ field_queries = g_new0 (EBookQuery **, entry->priv->n_search_fields - 1);
+
+ for (i = 0; i < entry->priv->n_search_fields - 1; i++) {
+ field_queries[i] = g_new0 (EBookQuery *, parts->len);
+ for (j = 0; j < parts->len; j++) {
+ field_queries[i][j] = e_book_query_field_test (entry->priv->search_fields[i], E_BOOK_QUERY_CONTAINS, g_array_index (parts, gchar *, j));
+ }
+ q[i] = e_book_query_and (parts->len, field_queries[i], TRUE);
+ }
+ g_array_free (parts, TRUE);
+
+ query = e_book_query_or (entry->priv->n_search_fields - 1, q, TRUE);
+
+ for (i = 0; i < entry->priv->n_search_fields - 1; i++) {
+ g_free (field_queries[i]);
+ }
+ g_free (field_queries);
+ g_free (q);
+
+ return query;
+}
+
+/**
+ * Given a widget, determines the height that text will normally be drawn.
+ */
+static guint
+entry_height (GtkWidget *widget)
+{
+ PangoLayout *layout;
+ int bound;
+ g_return_val_if_fail (widget != NULL, 0);
+ layout = gtk_widget_create_pango_layout (widget, NULL);
+ pango_layout_get_pixel_size (layout, NULL, &bound);
+ return bound;
+}
+
+/**
+ * Free a EContactEntyItem struct.
+ */
+static void
+e_contact_entry_item_free (EContactEntyItem *item)
+{
+ g_free (item->display_string);
+ g_free (item->identifier);
+ g_free (item);
+}
+
+/**
+ * Return a string representing a given EBook status code.
+ */
+static const char*
+stringify_ebook_error(const EBookStatus status)
+{
+ switch (status) {
+ case E_BOOK_ERROR_OK:
+ return _("Success");
+ case E_BOOK_ERROR_INVALID_ARG:
+ return _("An argument was invalid.");
+ case E_BOOK_ERROR_BUSY:
+ return _("The address book is busy.");
+ case E_BOOK_ERROR_REPOSITORY_OFFLINE:
+ return _("The address book is offline.");
+ case E_BOOK_ERROR_NO_SUCH_BOOK:
+ return _("The address book does not exist.");
+ case E_BOOK_ERROR_NO_SELF_CONTACT:
+ return _("The \"Me\" contact does not exist.");
+ case E_BOOK_ERROR_SOURCE_NOT_LOADED:
+ return _("The address book is not loaded.");
+ case E_BOOK_ERROR_SOURCE_ALREADY_LOADED:
+ return _("The address book is already loaded.");
+ case E_BOOK_ERROR_PERMISSION_DENIED:
+ return _("Permission was denied when accessing the address book.");
+ case E_BOOK_ERROR_CONTACT_NOT_FOUND:
+ return _("The contact was not found.");
+ case E_BOOK_ERROR_CONTACT_ID_ALREADY_EXISTS:
+ return _("This contact ID already exists.");
+ case E_BOOK_ERROR_PROTOCOL_NOT_SUPPORTED:
+ return _("The protocol is not supported.");
+ case E_BOOK_ERROR_CANCELLED:
+ return _("The operation was cancelled.");
+ case E_BOOK_ERROR_COULD_NOT_CANCEL:
+ return _("The operation could not be cancelled.");
+ case E_BOOK_ERROR_AUTHENTICATION_FAILED:
+ return _("The address book authentication failed.");
+ case E_BOOK_ERROR_AUTHENTICATION_REQUIRED:
+ return _("Authentication is required to access the address book and was not given.");
+ case E_BOOK_ERROR_TLS_NOT_AVAILABLE:
+ return _("A secure connection is not available.");
+ case E_BOOK_ERROR_CORBA_EXCEPTION:
+ return _("A CORBA error occurred whilst accessing the address book.");
+ case E_BOOK_ERROR_NO_SUCH_SOURCE:
+ return _("The address book source does not exist.");
+ case E_BOOK_ERROR_OTHER_ERROR:
+ return _("An unknown error occurred.");
+ default:
+ g_warning ("Unknown status %d", status);
+ return _("An unknown error occurred.");
+ }
+}
Added: trunk/cut-n-paste/e-contact-entry/e-contact-entry.h
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/e-contact-entry/e-contact-entry.h Mon Jun 16 20:28:51 2008
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2004 Ross Burton <ross burtonini com>
+ *
+ * e-contact-entry.h
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: Ross Burton <ross burtonini com>
+ */
+
+#ifndef CONTACT_ENTRY_H
+#define CONTACT_ENTRY_H
+
+#include <libedataserver/e-source-group.h>
+#include <libedataserver/e-source-list.h>
+#include <libebook/e-contact.h>
+#include <gtk/gtkentry.h>
+
+G_BEGIN_DECLS
+
+#define E_TYPE_CONTACT_ENTRY (e_contact_entry_get_type ())
+#define E_CONTACT_ENTRY(obj) (GTK_CHECK_CAST ((obj), e_contact_entry_get_type (), EContactEntry))
+#define E_CONTACT_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), e_contact_entry_get_type (), EContactEntryClass))
+#define E_IS_CONTACT_ENTRY(obj) (GTK_CHECK_TYPE (obj, e_contact_entry_get_type ()))
+#define E_IS_CONTACT_ENTRY_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), e_contact_entry_get_type ()))
+#define E_CONTACT_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_CONTACT_ENTRY_TYPE, EContactEntryClass))
+
+typedef struct EContactEntryPriv EContactEntryPriv;
+
+typedef struct {
+ GtkEntry parent;
+ EContactEntryPriv *priv;
+} EContactEntry;
+
+typedef struct {
+ GtkEntryClass parent_class;
+ /* Signal fired when a contact is selected. Use this over 'activate' */
+ void (*contact_selected) (GtkWidget *entry, EContact *contact, const char *identifier);
+ /* Signal fired when an async error occured */
+ void (*error) (GtkWidget *entry, const char* error);
+ /* Signal fired when the widget's state should change */
+ void (*state_change) (GtkWidget *entry, gboolean state);
+} EContactEntryClass;
+
+typedef struct {
+ char *display_string;
+ char *identifier; /* a "unique" identifier */
+} EContactEntyItem;
+
+/* A GList of EContactEntyItems */
+typedef GList* (*EContactEntryDisplayFunc) (EContact *contact, gpointer data);
+
+GType e_contact_entry_get_type (void);
+
+GtkWidget *e_contact_entry_new (void);
+
+void e_contact_entry_set_source_list (EContactEntry *entry, ESourceList *list);
+ESourceList *e_contact_entry_get_source_list (EContactEntry *entry);
+
+void e_contact_entry_set_complete_length(EContactEntry *entry, int length);
+int e_contact_entry_get_complete_length(EContactEntry *entry);
+
+void e_contact_entry_set_display_func (EContactEntry *entry, EContactEntryDisplayFunc func, gpointer func_data, GDestroyNotify destroy);
+
+void e_contact_entry_set_search_fields (EContactEntry *entry, const EContactField *fields);
+
+G_END_DECLS
+
+#endif /* CONTACT_ENTRY_H */
Added: trunk/cut-n-paste/e-contact-entry/econtactentry-marshal.list
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/e-contact-entry/econtactentry-marshal.list Mon Jun 16 20:28:51 2008
@@ -0,0 +1 @@
+VOID:OBJECT,STRING
Added: trunk/cut-n-paste/gconf-bridge/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/gconf-bridge/Makefile.am Mon Jun 16 20:28:51 2008
@@ -0,0 +1,8 @@
+AM_CPPFLAGS = \
+ $(PHONEMGR_CFLAGS)
+
+noinst_LTLIBRARIES = libgconf-bridge.la
+
+libgconf_bridge_la_SOURCES = \
+ gconf-bridge.c \
+ gconf-bridge.h
Added: trunk/cut-n-paste/gconf-bridge/gconf-bridge.c
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/gconf-bridge/gconf-bridge.c Mon Jun 16 20:28:51 2008
@@ -0,0 +1,1249 @@
+/*
+ * (C) 2005 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtkmessagedialog.h>
+#include <string.h>
+
+#include "gconf-bridge.h"
+
+struct _GConfBridge {
+ GConfClient *client;
+
+ GHashTable *bindings;
+};
+
+/* The data structures for the different kinds of bindings */
+typedef enum {
+ BINDING_PROP,
+ BINDING_WINDOW,
+ BINDING_LIST_STORE
+} BindingType;
+
+typedef struct {
+ BindingType type;
+ guint id;
+
+ gboolean delayed_mode;
+
+ char *key;
+ guint val_notify_id;
+ GSList *val_changes; /* List of changes made to GConf value,
+ that have not received change notification
+ yet. */
+
+ GObject *object;
+ GParamSpec *prop;
+ gulong prop_notify_id;
+
+ guint sync_timeout_id; /* Used in delayed mode */
+} PropBinding;
+
+typedef struct {
+ BindingType type;
+ guint id;
+
+ gboolean bind_size;
+ gboolean bind_pos;
+
+ char *key_prefix;
+
+ GtkWindow *window;
+ gulong configure_event_id;
+ gulong unmap_id;
+ guint sync_timeout_id;
+} WindowBinding;
+
+typedef struct {
+ BindingType type;
+ guint id;
+
+ char *key;
+ guint val_notify_id;
+ GSList *val_changes; /* List of changes made to GConf value,
+ that have not received change notification
+ yet. */
+
+ GtkListStore *list_store;
+ guint row_inserted_id;
+ guint row_changed_id;
+ guint row_deleted_id;
+ guint rows_reordered_id;
+
+ guint sync_idle_id;
+} ListStoreBinding;
+
+/* Some trickery to be able to treat the data structures generically */
+typedef union {
+ BindingType type;
+
+ PropBinding prop_binding;
+ WindowBinding window_binding;
+ ListStoreBinding list_store_binding;
+} Binding;
+
+/* Function prototypes */
+static void
+unbind (Binding *binding);
+
+#if !HAVE_DECL_GCONF_VALUE_COMPARE /* Not in headers in GConf < 2.13 */
+int gconf_value_compare (const GConfValue *value_a,
+ const GConfValue *value_b);
+#endif
+
+static GConfBridge *bridge = NULL; /* Global GConfBridge object */
+
+/* Free up all resources allocated by the GConfBridge. Called on exit. */
+static void
+destroy_bridge (void)
+{
+ g_hash_table_destroy (bridge->bindings);
+ g_object_unref (bridge->client);
+
+ g_free (bridge);
+}
+
+/**
+ * gconf_bridge_get
+ *
+ * Returns the #GConfBridge. This is a singleton object.
+ *
+ * Return value: The #GConfBridge.
+ **/
+GConfBridge *
+gconf_bridge_get (void)
+{
+ if (bridge)
+ return bridge;
+
+ gconf_bridge_install_default_error_handler ();
+
+ bridge = g_new (GConfBridge, 1);
+
+ bridge->client = gconf_client_get_default ();
+ bridge->bindings = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) unbind);
+
+ g_atexit (destroy_bridge);
+
+ return bridge;
+}
+
+/**
+ * gconf_bridge_get_client
+ * @bridge: A #GConfBridge
+ *
+ * Returns the #GConfClient used by @bridge. This is the same #GConfClient
+ * as returned by gconf_client_get_default().
+ *
+ * Return value: A #GConfClient.
+ **/
+GConfClient *
+gconf_bridge_get_client (GConfBridge *bridge)
+{
+ g_return_val_if_fail (bridge != NULL, NULL);
+
+ return bridge->client;
+}
+
+/* Generate an ID for a new binding */
+static guint
+new_id (void)
+{
+ static guint id_counter = 0;
+
+ id_counter++;
+
+ return id_counter;
+}
+
+/*
+ * Property bindings
+ */
+
+/* Syncs a value from GConf to an object property */
+static void
+prop_binding_sync_pref_to_prop (PropBinding *binding,
+ GConfValue *pref_value)
+{
+ GValue src_value, value;
+
+ /* Make sure we don't enter an infinite synchronizing loop */
+ g_signal_handler_block (binding->object, binding->prop_notify_id);
+
+ memset (&src_value, 0, sizeof (GValue));
+
+ /* First, convert GConfValue to GValue */
+ switch (pref_value->type) {
+ case GCONF_VALUE_STRING:
+ g_value_init (&src_value, G_TYPE_STRING);
+ g_value_set_string (&src_value,
+ gconf_value_get_string (pref_value));
+ break;
+ case GCONF_VALUE_INT:
+ g_value_init (&src_value, G_TYPE_INT);
+ g_value_set_int (&src_value,
+ gconf_value_get_int (pref_value));
+ break;
+ case GCONF_VALUE_BOOL:
+ g_value_init (&src_value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&src_value,
+ gconf_value_get_bool (pref_value));
+ break;
+ case GCONF_VALUE_FLOAT:
+ g_value_init (&src_value, G_TYPE_FLOAT);
+ g_value_set_float (&src_value,
+ gconf_value_get_float (pref_value));
+ break;
+ default:
+ g_warning ("prop_binding_sync_pref_to_prop: Unhandled value "
+ "type '%d'.\n", pref_value->type);
+
+ return;
+ }
+
+ /* Then convert to the type expected by the object, if necessary */
+ memset (&value, 0, sizeof (GValue));
+ g_value_init (&value,
+ G_PARAM_SPEC_VALUE_TYPE (binding->prop));
+
+ if (src_value.g_type != value.g_type) {
+ if (!g_value_transform (&src_value, &value)) {
+ g_warning ("prop_binding_sync_pref_to_prop: Failed to "
+ "transform a \"%s\" to a \"%s\".",
+ g_type_name (src_value.g_type),
+ g_type_name (value.g_type));
+
+ goto done;
+ }
+
+ g_object_set_property (binding->object,
+ binding->prop->name, &value);
+ } else {
+ g_object_set_property (binding->object,
+ binding->prop->name, &src_value);
+ }
+
+done:
+ g_value_unset (&src_value);
+ g_value_unset (&value);
+
+ g_signal_handler_unblock (binding->object, binding->prop_notify_id);
+}
+
+/* Syncs an object property to GConf */
+static void
+prop_binding_sync_prop_to_pref (PropBinding *binding)
+{
+ GValue value;
+ GConfValue *gconf_value;
+
+ memset (&value, 0, sizeof (GValue));
+
+ g_value_init (&value,
+ G_PARAM_SPEC_VALUE_TYPE (binding->prop));
+ g_object_get_property (binding->object,
+ binding->prop->name,
+ &value);
+
+ switch (value.g_type) {
+ case G_TYPE_STRING:
+ gconf_value = gconf_value_new (GCONF_VALUE_STRING);
+ gconf_value_set_string (gconf_value,
+ g_value_get_string (&value));
+ break;
+ case G_TYPE_INT:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_int (&value));
+ break;
+ case G_TYPE_UINT:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_uint (&value));
+ break;
+ case G_TYPE_LONG:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_long (&value));
+ break;
+ case G_TYPE_ULONG:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_ulong (&value));
+ break;
+ case G_TYPE_INT64:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_int64 (&value));
+ break;
+ case G_TYPE_UINT64:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_uint64 (&value));
+ break;
+ case G_TYPE_CHAR:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_char (&value));
+ break;
+ case G_TYPE_UCHAR:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_uchar (&value));
+ break;
+ case G_TYPE_ENUM:
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_enum (&value));
+ break;
+ case G_TYPE_BOOLEAN:
+ gconf_value = gconf_value_new (GCONF_VALUE_BOOL);
+ gconf_value_set_bool (gconf_value,
+ g_value_get_boolean (&value));
+ break;
+ case G_TYPE_DOUBLE:
+ gconf_value = gconf_value_new (GCONF_VALUE_FLOAT);
+#ifdef HAVE_CORBA_GCONF
+ /* FIXME we cast to a float explicitly as CORBA GConf
+ * uses doubles in its API, but treats them as floats
+ * when transporting them over CORBA. See #322837 */
+ gconf_value_set_float (gconf_value,
+ (float) g_value_get_double (&value));
+#else
+ gconf_value_set_float (gconf_value,
+ g_value_get_double (&value));
+#endif
+ break;
+ case G_TYPE_FLOAT:
+ gconf_value = gconf_value_new (GCONF_VALUE_FLOAT);
+ gconf_value_set_float (gconf_value,
+ g_value_get_float (&value));
+ break;
+ default:
+ if (g_type_is_a (value.g_type, G_TYPE_ENUM)) {
+ gconf_value = gconf_value_new (GCONF_VALUE_INT);
+ gconf_value_set_int (gconf_value,
+ g_value_get_enum (&value));
+ } else {
+ g_warning ("prop_binding_sync_prop_to_pref: "
+ "Unhandled value type '%s'.\n",
+ g_type_name (value.g_type));
+
+ goto done;
+ }
+
+ break;
+ }
+
+ /* Set to GConf */
+ gconf_client_set (bridge->client, binding->key, gconf_value, NULL);
+
+ /* Store until change notification comes in, so that we are able
+ * to ignore it */
+ binding->val_changes = g_slist_append (binding->val_changes,
+ gconf_value);
+
+done:
+ g_value_unset (&value);
+}
+
+/* Called when a GConf value bound to an object property has changed */
+static void
+prop_binding_pref_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ GConfValue *gconf_value;
+ PropBinding *binding;
+ GSList *l;
+
+ gconf_value = gconf_entry_get_value (entry);
+ if (!gconf_value)
+ return; /* NULL means that the value has been unset */
+
+ binding = (PropBinding *) user_data;
+
+ /* Check that this notification is not caused by sync_prop_to_pref() */
+ l = g_slist_find_custom (binding->val_changes,
+ gconf_value,
+ (GCompareFunc) gconf_value_compare);
+ if (l) {
+ gconf_value_free (l->data);
+
+ binding->val_changes = g_slist_delete_link
+ (binding->val_changes, l);
+
+ return;
+ }
+
+ prop_binding_sync_pref_to_prop (binding, gconf_value);
+}
+
+/* Performs a scheduled prop-to-pref sync for a prop binding in
+ * delay mode */
+static gboolean
+prop_binding_perform_scheduled_sync (PropBinding *binding)
+{
+ prop_binding_sync_prop_to_pref (binding);
+
+ binding->sync_timeout_id = 0;
+
+ g_object_unref (binding->object);
+
+ return FALSE;
+}
+
+#define PROP_BINDING_SYNC_DELAY 100 /* Delay for bindings with "delayed"
+ set to TRUE, in ms */
+
+/* Called when an object property has changed */
+static void
+prop_binding_prop_changed (GObject *object,
+ GParamSpec *param_spec,
+ PropBinding *binding)
+{
+ if (binding->delayed_mode) {
+ /* Just schedule a sync */
+ if (binding->sync_timeout_id == 0) {
+ /* We keep a reference on the object as long as
+ * we haven't synced yet to make sure we don't
+ * lose any data */
+ g_object_ref (binding->object);
+
+ binding->sync_timeout_id =
+ g_timeout_add
+ (PROP_BINDING_SYNC_DELAY,
+ (GSourceFunc)
+ prop_binding_perform_scheduled_sync,
+ binding);
+ }
+ } else {
+ /* Directly sync */
+ prop_binding_sync_prop_to_pref (binding);
+ }
+}
+
+/* Called when an object is destroyed */
+static void
+prop_binding_object_destroyed (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ PropBinding *binding;
+
+ binding = (PropBinding *) user_data;
+ binding->object = NULL; /* Don't do anything with the object
+ at unbind() */
+
+ g_hash_table_remove (bridge->bindings,
+ GUINT_TO_POINTER (binding->id));
+}
+
+/**
+ * gconf_bridge_bind_property_full
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ * @delayed_sync: TRUE if there should be a delay between property changes
+ * and syncs to GConf. Set to TRUE when binding to a rapidly-changing
+ * property, for example the "value" property on a #GtkAdjustment.
+ *
+ * Binds @key to @prop, causing them to have the same value at all times.
+ *
+ * The types of @key and @prop should be compatible. Floats and doubles, and
+ * ints, uints, longs, unlongs, int64s, uint64s, chars, uchars and enums
+ * can be matched up. Booleans and strings can only be matched to their
+ * respective types.
+ *
+ * On calling this function the current value of @key will be set to @prop.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_property_full (GConfBridge *bridge,
+ const char *key,
+ GObject *object,
+ const char *prop,
+ gboolean delayed_sync)
+{
+ GParamSpec *pspec;
+ PropBinding *binding;
+ char *signal;
+ GConfValue *val;
+
+ g_return_val_if_fail (bridge != NULL, 0);
+ g_return_val_if_fail (key != NULL, 0);
+ g_return_val_if_fail (G_IS_OBJECT (object), 0);
+ g_return_val_if_fail (prop != NULL, 0);
+
+ /* First, try to fetch the propertys GParamSpec off the object */
+ pspec = g_object_class_find_property
+ (G_OBJECT_GET_CLASS (object), prop);
+ if (G_UNLIKELY (pspec == NULL)) {
+ g_warning ("gconf_bridge_bind_property_full: A property \"%s\" "
+ "was not found. Please make sure you are passing "
+ "the right property name.", prop);
+
+ return 0;
+ }
+
+ /* GParamSpec found: All good, create new binding. */
+ binding = g_new (PropBinding, 1);
+
+ binding->type = BINDING_PROP;
+ binding->id = new_id ();
+ binding->delayed_mode = delayed_sync;
+ binding->val_changes = NULL;
+ binding->key = g_strdup (key);
+ binding->object = object;
+ binding->prop = pspec;
+ binding->sync_timeout_id = 0;
+
+ /* Watch GConf key */
+ binding->val_notify_id =
+ gconf_client_notify_add (bridge->client, key,
+ prop_binding_pref_changed,
+ binding, NULL, NULL);
+
+ /* Connect to property change notifications */
+ signal = g_strconcat ("notify::", prop, NULL);
+ binding->prop_notify_id =
+ g_signal_connect (object, signal,
+ G_CALLBACK (prop_binding_prop_changed),
+ binding);
+ g_free (signal);
+
+ /* Sync object to value from GConf, if set */
+ val = gconf_client_get (bridge->client, key, NULL);
+ if (val) {
+ prop_binding_sync_pref_to_prop (binding, val);
+ gconf_value_free (val);
+ }
+
+ /* Handle case where watched object gets destroyed */
+ g_object_weak_ref (object,
+ prop_binding_object_destroyed, binding);
+
+ /* Insert binding */
+ g_hash_table_insert (bridge->bindings,
+ GUINT_TO_POINTER (binding->id), binding);
+
+ /* Done */
+ return binding->id;
+}
+
+/* Unbinds a property binding */
+static void
+prop_binding_unbind (PropBinding *binding)
+{
+ if (binding->delayed_mode && binding->sync_timeout_id > 0) {
+ /* Perform any scheduled syncs */
+ g_source_remove (binding->sync_timeout_id);
+
+ /* The object will still be around as we have
+ * a reference */
+ prop_binding_perform_scheduled_sync (binding);
+ }
+
+ gconf_client_notify_remove (bridge->client,
+ binding->val_notify_id);
+ g_free (binding->key);
+
+ while (binding->val_changes) {
+ gconf_value_free (binding->val_changes->data);
+
+ binding->val_changes = g_slist_delete_link
+ (binding->val_changes, binding->val_changes);
+ }
+
+ /* The object might have been destroyed .. */
+ if (binding->object) {
+ g_signal_handler_disconnect (binding->object,
+ binding->prop_notify_id);
+
+ g_object_weak_unref (binding->object,
+ prop_binding_object_destroyed, binding);
+ }
+}
+
+/*
+ * Window bindings
+ */
+
+/* Performs a scheduled dimensions-to-prefs sync for a window binding */
+static gboolean
+window_binding_perform_scheduled_sync (WindowBinding *binding)
+{
+ if (binding->bind_size) {
+ int width, height;
+ char *key;
+ GdkWindowState state;
+
+ state = gdk_window_get_state (GTK_WIDGET (binding->window)->window);
+
+ if (state & GDK_WINDOW_STATE_MAXIMIZED) {
+ key = g_strconcat (binding->key_prefix, "_maximized", NULL);
+ gconf_client_set_bool (bridge->client, key, TRUE, NULL);
+ g_free (key);
+ } else {
+ gtk_window_get_size (binding->window, &width, &height);
+
+ key = g_strconcat (binding->key_prefix, "_width", NULL);
+ gconf_client_set_int (bridge->client, key, width, NULL);
+ g_free (key);
+
+ key = g_strconcat (binding->key_prefix, "_height", NULL);
+ gconf_client_set_int (bridge->client, key, height, NULL);
+ g_free (key);
+
+ key = g_strconcat (binding->key_prefix, "_maximized", NULL);
+ gconf_client_set_bool (bridge->client, key, FALSE, NULL);
+ g_free (key);
+ }
+ }
+
+ if (binding->bind_pos) {
+ int x, y;
+ char *key;
+
+ gtk_window_get_position (binding->window, &x, &y);
+
+ key = g_strconcat (binding->key_prefix, "_x", NULL);
+ gconf_client_set_int (bridge->client, key, x, NULL);
+ g_free (key);
+
+ key = g_strconcat (binding->key_prefix, "_y", NULL);
+ gconf_client_set_int (bridge->client, key, y, NULL);
+ g_free (key);
+ }
+
+ binding->sync_timeout_id = 0;
+
+ return FALSE;
+}
+
+#define WINDOW_BINDING_SYNC_DELAY 1000 /* Delay before syncing new window
+ dimensions to GConf, in ms */
+
+/* Called when the window han been resized or moved */
+static gboolean
+window_binding_configure_event_cb (GtkWindow *window,
+ GdkEventConfigure *event,
+ WindowBinding *binding)
+{
+ /* Schedule a sync */
+ if (binding->sync_timeout_id == 0) {
+ binding->sync_timeout_id =
+ g_timeout_add (WINDOW_BINDING_SYNC_DELAY,
+ (GSourceFunc)
+ window_binding_perform_scheduled_sync,
+ binding);
+ }
+
+ return FALSE;
+}
+
+/* Called when the window state is being changed */
+static gboolean
+window_binding_state_event_cb (GtkWindow *window,
+ GdkEventWindowState *event,
+ WindowBinding *binding)
+{
+ window_binding_perform_scheduled_sync (binding);
+
+ return FALSE;
+}
+
+/* Called when the window is being unmapped */
+static gboolean
+window_binding_unmap_cb (GtkWindow *window,
+ WindowBinding *binding)
+{
+ /* Force sync */
+ if (binding->sync_timeout_id > 0)
+ g_source_remove (binding->sync_timeout_id);
+
+ window_binding_perform_scheduled_sync (binding);
+
+ return FALSE;
+}
+
+/* Called when a window is destroyed */
+static void
+window_binding_window_destroyed (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ WindowBinding *binding;
+
+ binding = (WindowBinding *) user_data;
+ binding->window = NULL; /* Don't do anything with the window
+ at unbind() */
+
+ g_hash_table_remove (bridge->bindings,
+ GUINT_TO_POINTER (binding->id));
+}
+
+/**
+ * gconf_bridge_bind_window
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ * @bind_size: TRUE to bind the size of @window
+ * @bind_pos: TRUE to bind the position of @window
+ *
+ * On calling this function @window will be resized to the values
+ * specified by "@key_prefix<!-- -->_width" and "@key_prefix<!-- -->_height"
+ * and maximixed if "@key_prefix<!-- -->_maximized is TRUE if
+ * @bind_size is TRUE, and moved to the values specified by
+ * "@key_prefix<!-- -->_x" and "@key_prefix<!-- -->_y" if @bind_pos is TRUE.
+ * The respective GConf values will be updated when the window is resized
+ * and/or moved.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_window (GConfBridge *bridge,
+ const char *key_prefix,
+ GtkWindow *window,
+ gboolean bind_size,
+ gboolean bind_pos)
+{
+ WindowBinding *binding;
+
+ g_return_val_if_fail (bridge != NULL, 0);
+ g_return_val_if_fail (key_prefix != NULL, 0);
+ g_return_val_if_fail (GTK_IS_WINDOW (window), 0);
+
+ /* Create new binding. */
+ binding = g_new (WindowBinding, 1);
+
+ binding->type = BINDING_WINDOW;
+ binding->id = new_id ();
+ binding->bind_size = bind_size;
+ binding->bind_pos = bind_pos;
+ binding->key_prefix = g_strdup (key_prefix);
+ binding->window = window;
+ binding->sync_timeout_id = 0;
+
+ /* Set up GConf keys & sync window to GConf values */
+ if (bind_size) {
+ char *key;
+ GConfValue *width_val, *height_val, *maximized_val;
+
+ key = g_strconcat (key_prefix, "_width", NULL);
+ width_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ key = g_strconcat (key_prefix, "_height", NULL);
+ height_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ key = g_strconcat (key_prefix, "_maximized", NULL);
+ maximized_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ if (width_val && height_val) {
+ gtk_window_resize (window,
+ gconf_value_get_int (width_val),
+ gconf_value_get_int (height_val));
+
+ gconf_value_free (width_val);
+ gconf_value_free (height_val);
+ } else if (width_val) {
+ gconf_value_free (width_val);
+ } else if (height_val) {
+ gconf_value_free (height_val);
+ }
+
+ if (maximized_val) {
+ if (gconf_value_get_bool (maximized_val)) {
+ gtk_window_maximize (window);
+ }
+ gconf_value_free (maximized_val);
+ }
+ }
+
+ if (bind_pos) {
+ char *key;
+ GConfValue *x_val, *y_val;
+
+ key = g_strconcat (key_prefix, "_x", NULL);
+ x_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ key = g_strconcat (key_prefix, "_y", NULL);
+ y_val = gconf_client_get (bridge->client, key, NULL);
+ g_free (key);
+
+ if (x_val && y_val) {
+ gtk_window_move (window,
+ gconf_value_get_int (x_val),
+ gconf_value_get_int (y_val));
+
+ gconf_value_free (x_val);
+ gconf_value_free (y_val);
+ } else if (x_val) {
+ gconf_value_free (x_val);
+ } else if (y_val) {
+ gconf_value_free (y_val);
+ }
+ }
+
+ /* Connect to window size change notifications */
+ binding->configure_event_id =
+ g_signal_connect (window,
+ "configure-event",
+ G_CALLBACK
+ (window_binding_configure_event_cb),
+ binding);
+
+ binding->configure_event_id =
+ g_signal_connect (window,
+ "window_state_event",
+ G_CALLBACK
+ (window_binding_state_event_cb),
+ binding);
+ binding->unmap_id =
+ g_signal_connect (window,
+ "unmap",
+ G_CALLBACK (window_binding_unmap_cb),
+ binding);
+
+ /* Handle case where window gets destroyed */
+ g_object_weak_ref (G_OBJECT (window),
+ window_binding_window_destroyed, binding);
+
+ /* Insert binding */
+ g_hash_table_insert (bridge->bindings,
+ GUINT_TO_POINTER (binding->id), binding);
+
+ /* Done */
+ return binding->id;
+}
+
+/* Unbinds a window binding */
+static void
+window_binding_unbind (WindowBinding *binding)
+{
+ if (binding->sync_timeout_id > 0)
+ g_source_remove (binding->sync_timeout_id);
+
+ g_free (binding->key_prefix);
+
+ /* The window might have been destroyed .. */
+ if (binding->window) {
+ g_signal_handler_disconnect (binding->window,
+ binding->configure_event_id);
+ g_signal_handler_disconnect (binding->window,
+ binding->unmap_id);
+
+ g_object_weak_unref (G_OBJECT (binding->window),
+ window_binding_window_destroyed, binding);
+ }
+}
+
+/*
+ * List store bindings
+ */
+
+/* Fills a GtkListStore with the string list from @value */
+static void
+list_store_binding_sync_pref_to_store (ListStoreBinding *binding,
+ GConfValue *value)
+{
+ GSList *list, *l;
+ GtkTreeIter iter;
+
+ /* Make sure we don't enter an infinite synchronizing loop */
+ g_signal_handler_block (binding->list_store,
+ binding->row_inserted_id);
+ g_signal_handler_block (binding->list_store,
+ binding->row_deleted_id);
+
+ gtk_list_store_clear (binding->list_store);
+
+ list = gconf_value_get_list (value);
+ for (l = list; l; l = l->next) {
+ GConfValue *l_value;
+ const char *string;
+
+ l_value = (GConfValue *) l->data;
+ string = gconf_value_get_string (l_value);
+
+ gtk_list_store_insert_with_values (binding->list_store,
+ &iter, -1,
+ 0, string,
+ -1);
+ }
+
+ g_signal_handler_unblock (binding->list_store,
+ binding->row_inserted_id);
+ g_signal_handler_unblock (binding->list_store,
+ binding->row_deleted_id);
+}
+
+/* Sets a GConf value to the contents of a GtkListStore */
+static gboolean
+list_store_binding_sync_store_to_pref (ListStoreBinding *binding)
+{
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter;
+ GSList *list;
+ int res;
+ GConfValue *gconf_value;
+
+ tree_model = GTK_TREE_MODEL (binding->list_store);
+
+ /* Build list */
+ list = NULL;
+ res = gtk_tree_model_get_iter_first (tree_model, &iter);
+ while (res) {
+ char *string;
+ GConfValue *tmp_value;
+
+ gtk_tree_model_get (tree_model, &iter,
+ 0, &string, -1);
+
+ tmp_value = gconf_value_new (GCONF_VALUE_STRING);
+ gconf_value_set_string (tmp_value, string);
+
+ list = g_slist_append (list, tmp_value);
+
+ res = gtk_tree_model_iter_next (tree_model, &iter);
+ }
+
+ /* Create value */
+ gconf_value = gconf_value_new (GCONF_VALUE_LIST);
+ gconf_value_set_list_type (gconf_value, GCONF_VALUE_STRING);
+ gconf_value_set_list_nocopy (gconf_value, list);
+
+ /* Set */
+ gconf_client_set (bridge->client, binding->key, gconf_value, NULL);
+
+ /* Store until change notification comes in, so that we are able
+ * to ignore it */
+ binding->val_changes = g_slist_append (binding->val_changes,
+ gconf_value);
+
+ binding->sync_idle_id = 0;
+
+ g_object_unref (binding->list_store);
+
+ return FALSE;
+}
+
+/* Pref changed: sync */
+static void
+list_store_binding_pref_changed (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ GConfValue *gconf_value;
+ ListStoreBinding *binding;
+ GSList *l;
+
+ gconf_value = gconf_entry_get_value (entry);
+ if (!gconf_value)
+ return; /* NULL means that the value has been unset */
+
+ binding = (ListStoreBinding *) user_data;
+
+ /* Check that this notification is not caused by
+ * sync_store_to_pref() */
+ l = g_slist_find_custom (binding->val_changes,
+ gconf_value,
+ (GCompareFunc) gconf_value_compare);
+ if (l) {
+ gconf_value_free (l->data);
+
+ binding->val_changes = g_slist_delete_link
+ (binding->val_changes, l);
+
+ return;
+ }
+
+ list_store_binding_sync_pref_to_store (binding, gconf_value);
+}
+
+/* Called when an object is destroyed */
+static void
+list_store_binding_store_destroyed (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ ListStoreBinding *binding;
+
+ binding = (ListStoreBinding *) user_data;
+ binding->list_store = NULL; /* Don't do anything with the store
+ at unbind() */
+
+ g_hash_table_remove (bridge->bindings,
+ GUINT_TO_POINTER (binding->id));
+}
+
+/* List store changed: Sync */
+static void
+list_store_binding_store_changed_cb (ListStoreBinding *binding)
+{
+ if (binding->sync_idle_id == 0) {
+ g_object_ref (binding->list_store);
+
+ binding->sync_idle_id = g_idle_add
+ ((GSourceFunc) list_store_binding_sync_store_to_pref,
+ binding);
+ }
+}
+
+/**
+ * gconf_bridge_bind_string_list_store
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @list_store: A #GtkListStore
+ *
+ * On calling this function single string column #GtkListStore @list_store
+ * will be kept synchronized with the GConf string list value pointed to by
+ * @key. On calling this function @list_store will be populated with the
+ * strings specified by the value of @key.
+ *
+ * Return value: The ID of the new binding.
+ **/
+guint
+gconf_bridge_bind_string_list_store (GConfBridge *bridge,
+ const char *key,
+ GtkListStore *list_store)
+{
+ GtkTreeModel *tree_model;
+ gboolean have_one_column, is_string_column;
+ ListStoreBinding *binding;
+ GConfValue *val;
+
+ g_return_val_if_fail (bridge != NULL, 0);
+ g_return_val_if_fail (key != NULL, 0);
+ g_return_val_if_fail (GTK_IS_LIST_STORE (list_store), 0);
+
+ /* Check list store suitability */
+ tree_model = GTK_TREE_MODEL (list_store);
+ have_one_column = (gtk_tree_model_get_n_columns (tree_model) == 1);
+ is_string_column = (gtk_tree_model_get_column_type
+ (tree_model, 0) == G_TYPE_STRING);
+ if (G_UNLIKELY (!have_one_column || !is_string_column)) {
+ g_warning ("gconf_bridge_bind_string_list_store: Only "
+ "GtkListStores with exactly one string column are "
+ "supported.");
+
+ return 0;
+ }
+
+ /* Create new binding. */
+ binding = g_new (ListStoreBinding, 1);
+
+ binding->type = BINDING_LIST_STORE;
+ binding->id = new_id ();
+ binding->key = g_strdup (key);
+ binding->val_changes = NULL;
+ binding->list_store = list_store;
+ binding->sync_idle_id = 0;
+
+ /* Watch GConf key */
+ binding->val_notify_id =
+ gconf_client_notify_add (bridge->client, key,
+ list_store_binding_pref_changed,
+ binding, NULL, NULL);
+
+ /* Connect to ListStore change notifications */
+ binding->row_inserted_id =
+ g_signal_connect_swapped (list_store, "row-inserted",
+ G_CALLBACK
+ (list_store_binding_store_changed_cb),
+ binding);
+ binding->row_changed_id =
+ g_signal_connect_swapped (list_store, "row-inserted",
+ G_CALLBACK
+ (list_store_binding_store_changed_cb),
+ binding);
+ binding->row_deleted_id =
+ g_signal_connect_swapped (list_store, "row-inserted",
+ G_CALLBACK
+ (list_store_binding_store_changed_cb),
+ binding);
+ binding->rows_reordered_id =
+ g_signal_connect_swapped (list_store, "row-inserted",
+ G_CALLBACK
+ (list_store_binding_store_changed_cb),
+ binding);
+
+ /* Sync object to value from GConf, if set */
+ val = gconf_client_get (bridge->client, key, NULL);
+ if (val) {
+ list_store_binding_sync_pref_to_store (binding, val);
+ gconf_value_free (val);
+ }
+
+ /* Handle case where watched object gets destroyed */
+ g_object_weak_ref (G_OBJECT (list_store),
+ list_store_binding_store_destroyed, binding);
+
+ /* Insert binding */
+ g_hash_table_insert (bridge->bindings,
+ GUINT_TO_POINTER (binding->id), binding);
+
+ /* Done */
+ return binding->id;
+}
+
+/* Unbinds a list store binding */
+static void
+list_store_binding_unbind (ListStoreBinding *binding)
+{
+ /* Perform any scheduled syncs */
+ if (binding->sync_idle_id > 0) {
+ g_source_remove (binding->sync_idle_id);
+
+ /* The store will still be around as we added a reference */
+ list_store_binding_sync_store_to_pref (binding);
+ }
+
+ g_free (binding->key);
+
+ while (binding->val_changes) {
+ gconf_value_free (binding->val_changes->data);
+
+ binding->val_changes = g_slist_delete_link
+ (binding->val_changes, binding->val_changes);
+ }
+
+ /* The store might have been destroyed .. */
+ if (binding->list_store) {
+ g_signal_handler_disconnect (binding->list_store,
+ binding->row_inserted_id);
+ g_signal_handler_disconnect (binding->list_store,
+ binding->row_changed_id);
+ g_signal_handler_disconnect (binding->list_store,
+ binding->row_deleted_id);
+ g_signal_handler_disconnect (binding->list_store,
+ binding->rows_reordered_id);
+
+ g_object_weak_unref (G_OBJECT (binding->list_store),
+ list_store_binding_store_destroyed,
+ binding);
+ }
+}
+
+/*
+ * Generic unbinding
+ */
+
+/* Unbinds a binding */
+static void
+unbind (Binding *binding)
+{
+ /* Call specialized unbinding function */
+ switch (binding->type) {
+ case BINDING_PROP:
+ prop_binding_unbind ((PropBinding *) binding);
+ break;
+ case BINDING_WINDOW:
+ window_binding_unbind ((WindowBinding *) binding);
+ break;
+ case BINDING_LIST_STORE:
+ list_store_binding_unbind ((ListStoreBinding *) binding);
+ break;
+ default:
+ g_warning ("Unknown binding type '%d'\n", binding->type);
+ break;
+ }
+
+ g_free (binding);
+}
+
+/**
+ * gconf_bridge_unbind
+ * @bridge: A #GConfBridge
+ * @binding_id: The ID of the binding to be removed
+ *
+ * Removes the binding with ID @binding_id.
+ **/
+void
+gconf_bridge_unbind (GConfBridge *bridge,
+ guint binding_id)
+{
+ g_return_if_fail (bridge != NULL);
+ g_return_if_fail (binding_id > 0);
+
+ /* This will trigger the hash tables value destruction
+ * function, which will take care of further cleanup */
+ g_hash_table_remove (bridge->bindings,
+ GUINT_TO_POINTER (binding_id));
+}
+
+/*
+ * Error handling
+ */
+
+/* This is the same dialog as used in eel */
+static void
+error_handler (GConfClient *client,
+ GError *error)
+{
+ static gboolean shown_dialog = FALSE;
+
+ g_warning ("GConf error:\n %s", error->message);
+
+ if (!shown_dialog) {
+ char *message;
+ GtkWidget *dlg;
+
+ message = g_strdup_printf (_("GConf error: %s"),
+ error->message);
+ dlg = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ message);
+ g_free (message);
+
+ gtk_message_dialog_format_secondary_text
+ (GTK_MESSAGE_DIALOG (dlg),
+ _("All further errors shown only on terminal."));
+ gtk_window_set_title (GTK_WINDOW (dlg), "");
+
+ gtk_dialog_run (GTK_DIALOG (dlg));
+
+ gtk_widget_destroy (dlg);
+
+ shown_dialog = TRUE;
+ }
+}
+
+/**
+ * gconf_bridge_install_default_error_handler
+ *
+ * Sets up the default error handler. Any unhandled GConf errors will
+ * automatically be handled by presenting the user an error dialog.
+ **/
+void
+gconf_bridge_install_default_error_handler (void)
+{
+ gconf_client_set_global_default_error_handler (error_handler);
+}
Added: trunk/cut-n-paste/gconf-bridge/gconf-bridge.h
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/gconf-bridge/gconf-bridge.h Mon Jun 16 20:28:51 2008
@@ -0,0 +1,117 @@
+/*
+ * (C) 2005 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn openedhand com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GCONF_BRIDGE_H__
+#define __GCONF_BRIDGE_H__
+
+#include <gconf/gconf-client.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkliststore.h>
+
+G_BEGIN_DECLS
+
+void gconf_bridge_install_default_error_handler (void);
+
+typedef struct _GConfBridge GConfBridge;
+
+GConfBridge *gconf_bridge_get (void);
+
+GConfClient *gconf_bridge_get_client (GConfBridge *bridge);
+
+guint gconf_bridge_bind_property_full (GConfBridge *bridge,
+ const char *key,
+ GObject *object,
+ const char *prop,
+ gboolean delayed_sync);
+
+/**
+ * gconf_bridge_bind_property
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ *
+ * Binds @key to @prop without delays, causing them to have the same value at all times. See
+ * #gconf_bridge_bind_property_full for more details.
+ *
+ **/
+#define gconf_bridge_bind_property(bridge, key, object, prop) \
+ gconf_bridge_bind_property_full ((bridge), (key), \
+ (object), (prop), FALSE)
+
+/**
+ * gconf_bridge_bind_property_delayed
+ * @bridge: A #GConfBridge
+ * @key: A GConf key to be bound
+ * @object: A #GObject
+ * @prop: The property of @object to be bound
+ *
+ * Binds @key to @prop with a delay, causing them to have the same value at all
+ * times. See #gconf_bridge_bind_property_full for more details.
+ **/
+#define gconf_bridge_bind_property_delayed(bridge, key, object, prop) \
+ gconf_bridge_bind_property_full ((bridge), (key), \
+ (object), (prop), TRUE)
+
+guint gconf_bridge_bind_window (GConfBridge *bridge,
+ const char *key_prefix,
+ GtkWindow *window,
+ gboolean bind_size,
+ gboolean bind_pos);
+
+/**
+ * gconf_bridge_bind_window_size
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ *
+ * On calling this function @window will be resized to the values specified by
+ * "@key_prefix<!-- -->_width" and "@key_prefix<!-- -->_height". The respective
+ * GConf values will be updated when the window is resized. See
+ * #gconf_bridge_bind_window for more details.
+ **/
+#define gconf_bridge_bind_window_size(bridge, key_prefix, window) \
+ gconf_bridge_bind_window ((bridge), (key_prefix), (window), TRUE, FALSE)
+
+/**
+ * gconf_bridge_bind_window_pos
+ * @bridge: A #GConfBridge
+ * @key_prefix: The prefix of the GConf keys
+ * @window: A #GtkWindow
+ *
+ * On calling this function @window will be moved to the values specified by
+ * "@key_prefix<!-- -->_x" and "@key_prefix<!-- -->_y". The respective GConf
+ * values will be updated when the window is moved. See
+ * #gconf_bridge_bind_window for more details.
+ **/
+#define gconf_bridge_bind_window_pos(bridge, key_prefix, window) \
+ gconf_bridge_bind_window ((bridge), (key_prefix), (window), FALSE, TRUE)
+
+guint gconf_bridge_bind_string_list_store (GConfBridge *bridge,
+ const char *key,
+ GtkListStore *list_store);
+
+void gconf_bridge_unbind (GConfBridge *bridge,
+ guint binding_id);
+
+G_END_DECLS
+
+#endif /* __GCONF_BRIDGE_H__ */
Added: trunk/cut-n-paste/update-from-egg.sh
==============================================================================
--- (empty file)
+++ trunk/cut-n-paste/update-from-egg.sh Mon Jun 16 20:28:51 2008
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+function die() {
+ echo $*
+ exit 1
+}
+
+if test -z "$EGGDIR"; then
+ echo "Must set EGGDIR"
+ exit 1
+fi
+
+if test -z "$EGGFILES"; then
+ echo "Must set EGGFILES"
+ exit 1
+fi
+
+for FILE in $EGGFILES; do
+ if cmp -s $EGGDIR/$FILE $FILE; then
+ echo "File $FILE is unchanged"
+ else
+ cp $EGGDIR/$FILE $FILE || die "Could not move $EGGDIR/$FILE to $FILE"
+ echo "Updated $FILE"
+ fi
+done
Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am (original)
+++ trunk/src/Makefile.am Mon Jun 16 20:28:51 2008
@@ -7,6 +7,10 @@
-DPACKAGE="\"$(PACKAGE)\"" \
-I$(srcdir)/../libgsm \
-I$(builddir)/../libgsm \
+ -I$(srcdir)/../cut-n-paste/e-contact-entry \
+ -I$(builddir)/../cut-n-paste/e-contact-entry \
+ -I$(srcdir)/../cut-n-paste/gconf-bridge \
+ -I$(builddir)/../cut-n-paste/gconf-bridge \
-I$(builddir)/src
bin_PROGRAMS = gnome-phone-manager
@@ -21,18 +25,16 @@
connection.c \
e-phone-entry.c \
e-phone-entry.h \
- $(CLA_FILES) \
- $(MARSHALFILES) \
phonemgr-object.c \
phonemgr-object.h \
phonemgr-chooser-button.c \
- phonemgr-chooser-button.h \
- gconf-bridge.c \
- gconf-bridge.h
+ phonemgr-chooser-button.h
gnome_phone_manager_LDADD = \
$(PHONEMGR_LIBS) \
- ../libgsm/libgsmwrap.la
+ ../libgsm/libgsmwrap.la \
+ ../cut-n-paste/e-contact-entry/libecontact-entry.la \
+ ../cut-n-paste/gconf-bridge//libgconf-bridge.la
BUILT_SOURCES = phone-manager-interface.h
@@ -46,25 +48,13 @@
# The test program
noinst_PROGRAMS = test-entry
-test_entry_SOURCES = test-entry.c e-phone-entry.c e-phone-entry.h $(CLA_FILES) $(MARSHALFILES)
-test_entry_LDADD = $(PHONEMGR_LIBS)
+test_entry_SOURCES = test-entry.c e-phone-entry.c e-phone-entry.h
+test_entry_LDADD = $(PHONEMGR_LIBS) \
+ ../cut-n-paste/e-contact-entry/libecontact-entry.la \
+ ../cut-n-paste/gconf-bridge/libgconf-bridge.la
-MARSHALFILES = econtactentry-marshal.c econtactentry-marshal.h
BUILT_SOURCES += $(MARSHALFILES)
-econtactentry-marshal.c: econtactentry-marshal.h
- ( $(GLIB_GENMARSHAL) --prefix=econtactentry_marshal $(srcdir)/econtactentry-marshal.list --header --body > econtactentry-marshal.c )
-econtactentry-marshal.h: econtactentry-marshal.list
- ( $(GLIB_GENMARSHAL) --prefix=econtactentry_marshal $(srcdir)/econtactentry-marshal.list --header > econtactentry-marshal.h )
-
-CLA_FILES = e-contact-entry.h e-contact-entry.c
-CLADIR = $(srcdir)/../../contact-lookup-applet/src/
-GCONF_BRIDGE_FILES = gconf-bridge.h gconf-bridge.c
-GCONF_BRIDGE_DIR = $(srcdir)/../../libgconf-bridge/libgconf-bridge/
-regenerate-built-sources:
- EGGFILES="$(CLA_FILES) econtactentry-marshal.list" EGGDIR="$(CLADIR)" $(srcdir)/update-from-egg.sh || true
- EGGFILES="$(GCONF_BRIDGE_FILES)" EGGDIR="$(GCONF_BRIDGE_DIR)" $(srcdir)/update-from-egg.sh || true
-
EXTRA_DIST = gnome-phone-manager.desktop.in org_gnome_phone_Manager.xml econtactentry-marshal.list
CLEANFILES = $(BUILT_SOURCES) $(desktop_DATA)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]