phonemgr r303 - in trunk: . cut-n-paste cut-n-paste/e-contact-entry cut-n-paste/gconf-bridge src



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]