[evolution] Add EDataCapture.



commit c64602b39797e4d1ffdc23766b2caaf5a0be000b
Author: Matthew Barnes <mbarnes redhat com>
Date:   Tue Apr 23 15:06:01 2013 -0400

    Add EDataCapture.
    
    EDataCapture is a GConverter that captures data until the end of the
    input data is seen, then emits a "finished" signal with the captured
    data in a GBytes instance.
    
    When used with GConverterInputStream or GConverterOutputStream, an
    EDataCapture can discreetly capture stream content for the purpose
    of caching.

 doc/reference/libeutil/libeutil-docs.sgml    |    1 +
 doc/reference/libeutil/libeutil-sections.txt |   19 ++
 doc/reference/libeutil/libeutil.types        |    1 +
 e-util/Makefile.am                           |    2 +
 e-util/e-data-capture.c                      |  359 ++++++++++++++++++++++++++
 e-util/e-data-capture.h                      |   79 ++++++
 e-util/e-util.h                              |    1 +
 7 files changed, 462 insertions(+), 0 deletions(-)
---
diff --git a/doc/reference/libeutil/libeutil-docs.sgml b/doc/reference/libeutil/libeutil-docs.sgml
index 2442b43..2d705be 100644
--- a/doc/reference/libeutil/libeutil-docs.sgml
+++ b/doc/reference/libeutil/libeutil-docs.sgml
@@ -230,6 +230,7 @@
     <xi:include href="xml/e-cell-renderer-color.xml"/>
     <xi:include href="xml/e-charset-combo-box.xml"/>
     <xi:include href="xml/e-contact-store.xml"/>
+    <xi:include href="xml/e-data-capture.xml"/>
     <xi:include href="xml/e-dateedit.xml"/>
     <xi:include href="xml/e-destination-store.xml"/>
     <xi:include href="xml/e-focus-tracker.xml"/>
diff --git a/doc/reference/libeutil/libeutil-sections.txt b/doc/reference/libeutil/libeutil-sections.txt
index cc80cd6..0c89894 100644
--- a/doc/reference/libeutil/libeutil-sections.txt
+++ b/doc/reference/libeutil/libeutil-sections.txt
@@ -1406,6 +1406,25 @@ EContactStorePrivate
 </SECTION>
 
 <SECTION>
+<FILE>e-data-capture</FILE>
+<TITLE>EDataCapture</TITLE>
+EDataCapture
+e_data_capture_new
+e_data_capture_ref_main_context
+<SUBSECTION Standard>
+E_DATA_CAPTURE
+E_IS_DATA_CAPTURE
+E_TYPE_DATA_CAPTURE
+E_DATA_CAPTURE_CLASS
+E_IS_DATA_CAPTURE_CLASS
+E_DATA_CAPTURE_GET_CLASS
+EDataCaptureClass
+e_data_capture_get_type
+<SUBSECTION Private>
+EDataCapturePrivate
+</SECTION>
+
+<SECTION>
 <FILE>e-dateedit</FILE>
 <TITLE>EDateEdit</TITLE>
 EDateEditGetTimeCallback
diff --git a/doc/reference/libeutil/libeutil.types b/doc/reference/libeutil/libeutil.types
index bd1ec8d..bd867ba 100644
--- a/doc/reference/libeutil/libeutil.types
+++ b/doc/reference/libeutil/libeutil.types
@@ -59,6 +59,7 @@ e_client_selector_get_type
 e_config_get_type
 e_config_hook_get_type
 e_contact_store_get_type
+e_data_capture_get_type
 e_date_edit_get_type
 e_destination_store_get_type
 e_event_get_type
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 84331e2..e7a8f79 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -168,6 +168,7 @@ eutilinclude_HEADERS =  \
        e-client-selector.h \
        e-config.h \
        e-contact-store.h \
+       e-data-capture.h \
        e-dateedit.h \
        e-datetime-format.h \
        e-destination-store.h \
@@ -415,6 +416,7 @@ libeutil_la_SOURCES = \
        e-client-selector.c \
        e-config.c \
        e-contact-store.c \
+       e-data-capture.c \
        e-dateedit.c \
        e-datetime-format.c \
        e-destination-store.c \
diff --git a/e-util/e-data-capture.c b/e-util/e-data-capture.c
new file mode 100644
index 0000000..098b18b
--- /dev/null
+++ b/e-util/e-data-capture.c
@@ -0,0 +1,359 @@
+/*
+ * e-data-capture.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-data-capture
+ * @include: e-util/e-util.h
+ * @short_description: Capture data from streams
+ *
+ * #EDataCapture is a #GConverter that captures data until the end of
+ * the input data is seen, then emits a #EDataCapture:finished signal
+ * with the captured data in a #GBytes instance.
+ *
+ * When used with #GConverterInputStream or #GConverterOutputStream,
+ * an #EDataCapture can discreetly capture the stream content for the
+ * purpose of caching.
+ **/
+
+#include "e-data-capture.h"
+
+#include <string.h>
+
+#define E_DATA_CAPTURE_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_DATA_CAPTURE, EDataCapturePrivate))
+
+typedef struct _SignalClosure SignalClosure;
+
+struct _EDataCapturePrivate {
+       GMainContext *main_context;
+       GByteArray *byte_array;
+       GMutex byte_array_lock;
+};
+
+struct _SignalClosure {
+       GWeakRef data_capture;
+       GBytes *data;
+};
+
+enum {
+       PROP_0,
+       PROP_MAIN_CONTEXT
+};
+
+enum {
+       FINISHED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+/* Forward Declarations */
+static void    e_data_capture_converter_init   (GConverterIface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+       EDataCapture,
+       e_data_capture,
+       G_TYPE_OBJECT,
+       G_IMPLEMENT_INTERFACE (
+               G_TYPE_CONVERTER,
+               e_data_capture_converter_init))
+
+static void
+signal_closure_free (SignalClosure *signal_closure)
+{
+       g_weak_ref_set (&signal_closure->data_capture, NULL);
+       g_bytes_unref (signal_closure->data);
+
+       g_slice_free (SignalClosure, signal_closure);
+}
+
+static gboolean
+data_capture_emit_finished_idle_cb (gpointer user_data)
+{
+       SignalClosure *signal_closure = user_data;
+       EDataCapture *data_capture;
+
+       data_capture = g_weak_ref_get (&signal_closure->data_capture);
+
+       if (data_capture != NULL) {
+               g_signal_emit (
+                       data_capture,
+                       signals[FINISHED], 0,
+                       signal_closure->data);
+               g_object_unref (data_capture);
+       }
+
+       return FALSE;
+}
+
+static void
+data_capture_set_main_context (EDataCapture *data_capture,
+                               GMainContext *main_context)
+{
+       g_return_if_fail (data_capture->priv->main_context == NULL);
+
+       if (main_context != NULL)
+               g_main_context_ref (main_context);
+       else
+               main_context = g_main_context_ref_thread_default ();
+
+       data_capture->priv->main_context = main_context;
+}
+
+static void
+data_capture_set_property (GObject *object,
+                           guint property_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_MAIN_CONTEXT:
+                       data_capture_set_main_context (
+                               E_DATA_CAPTURE (object),
+                               g_value_get_boxed (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+data_capture_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_MAIN_CONTEXT:
+                       g_value_take_boxed (
+                               value,
+                               e_data_capture_ref_main_context (
+                               E_DATA_CAPTURE (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+data_capture_finalize (GObject *object)
+{
+       EDataCapturePrivate *priv;
+
+       priv = E_DATA_CAPTURE_GET_PRIVATE (object);
+
+       g_main_context_unref (priv->main_context);
+
+       g_byte_array_free (priv->byte_array, TRUE);
+       g_mutex_clear (&priv->byte_array_lock);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_data_capture_parent_class)->finalize (object);
+}
+
+static GConverterResult
+data_capture_convert (GConverter *converter,
+                      gconstpointer inbuf,
+                      gsize inbuf_size,
+                      gpointer outbuf,
+                      gsize outbuf_size,
+                      GConverterFlags flags,
+                      gsize *bytes_read,
+                      gsize *bytes_written,
+                      GError **error)
+{
+       EDataCapture *data_capture;
+       GConverterResult result;
+
+       data_capture = E_DATA_CAPTURE (converter);
+
+       /* Output buffer needs to be at least as large as the input buffer.
+        * The error message should never make it to the user interface so
+        * no need to translate. */
+       if (outbuf_size < inbuf_size) {
+               g_set_error_literal (
+                       error, G_IO_ERROR,
+                       G_IO_ERROR_NO_SPACE,
+                       "EDataCapture needs more space");
+               return G_CONVERTER_ERROR;
+       }
+
+       memcpy (outbuf, inbuf, inbuf_size);
+       *bytes_read = *bytes_written = inbuf_size;
+
+       g_mutex_lock (&data_capture->priv->byte_array_lock);
+
+       g_byte_array_append (
+               data_capture->priv->byte_array, inbuf, inbuf_size);
+
+       if ((flags & G_CONVERTER_INPUT_AT_END) != 0) {
+               GSource *idle_source;
+               GMainContext *main_context;
+               SignalClosure *signal_closure;
+
+               signal_closure = g_slice_new0 (SignalClosure);
+               g_weak_ref_set (&signal_closure->data_capture, data_capture);
+               signal_closure->data = g_bytes_new (
+                       data_capture->priv->byte_array->data,
+                       data_capture->priv->byte_array->len);
+
+               main_context = e_data_capture_ref_main_context (data_capture);
+
+               idle_source = g_idle_source_new ();
+               g_source_set_callback (
+                       idle_source,
+                       data_capture_emit_finished_idle_cb,
+                       signal_closure,
+                       (GDestroyNotify) signal_closure_free);
+               g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE);
+               g_source_attach (idle_source, main_context);
+               g_source_unref (idle_source);
+
+               g_main_context_unref (main_context);
+       }
+
+       g_mutex_unlock (&data_capture->priv->byte_array_lock);
+
+       if ((flags & G_CONVERTER_INPUT_AT_END) != 0)
+               result = G_CONVERTER_FINISHED;
+       else if ((flags & G_CONVERTER_FLUSH) != 0)
+               result = G_CONVERTER_FLUSHED;
+       else
+               result = G_CONVERTER_CONVERTED;
+
+       return result;
+}
+
+static void
+data_capture_reset (GConverter *converter)
+{
+       EDataCapture *data_capture;
+
+       data_capture = E_DATA_CAPTURE (converter);
+
+       g_mutex_lock (&data_capture->priv->byte_array_lock);
+
+       g_byte_array_set_size (data_capture->priv->byte_array, 0);
+
+       g_mutex_unlock (&data_capture->priv->byte_array_lock);
+}
+
+static void
+e_data_capture_class_init (EDataCaptureClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (EDataCapturePrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = data_capture_set_property;
+       object_class->get_property = data_capture_get_property;
+       object_class->finalize = data_capture_finalize;
+
+       /**
+        * EDataCapture:main-context:
+        *
+        * The #GMainContext from which to emit the #EDataCapture::finished
+        * signal.
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_MAIN_CONTEXT,
+               g_param_spec_boxed (
+                       "main-context",
+                       "Main Context",
+                       "The main loop context from "
+                       "which to emit the 'finished' signal",
+                       G_TYPE_MAIN_CONTEXT,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EDataCapture::finished:
+        * @data_capture: the #EDataCapture that received the signal
+        * @data: the captured data
+        *
+        * The ::finished signal is emitted when there is no more input
+        * data to be captured.
+        **/
+       signals[FINISHED] = g_signal_new (
+               "finished",
+               G_TYPE_FROM_CLASS (class),
+               G_SIGNAL_RUN_FIRST,
+               G_STRUCT_OFFSET (EDataCaptureClass, finished),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 1,
+               G_TYPE_BYTES);
+}
+
+static void
+e_data_capture_converter_init (GConverterIface *interface)
+{
+       interface->convert = data_capture_convert;
+       interface->reset = data_capture_reset;
+}
+
+static void
+e_data_capture_init (EDataCapture *data_capture)
+{
+       data_capture->priv = E_DATA_CAPTURE_GET_PRIVATE (data_capture);
+
+       data_capture->priv->byte_array = g_byte_array_new ();
+       g_mutex_init (&data_capture->priv->byte_array_lock);
+}
+
+/**
+ * e_data_capture_new:
+ * @main_context: a #GMainContext, or %NULL
+ *
+ * Creates a new #EDataCapture.  If @main_context is %NULL, then the
+ * #EDataCapture:finished signal will be emitted from the thread-default
+ * #GMainContext for this thread.
+ *
+ * Returns: an #EDataCapture
+ **/
+EDataCapture *
+e_data_capture_new (GMainContext *main_context)
+{
+       return g_object_new (
+               E_TYPE_DATA_CAPTURE,
+               "main-context", main_context, NULL);
+}
+
+/**
+ * e_data_capture_ref_main_context:
+ * @data_capture: an #EDataCapture
+ *
+ * Returns the #GMainContext from which the #EDataCapture:finished signal
+ * is emitted.
+ *
+ * The returned #GMainContext is referenced for thread-safety and must be
+ * unreferenced with g_main_context_unref() when finished with it.
+ *
+ * Returns: a #GMainContext
+ **/
+GMainContext *
+e_data_capture_ref_main_context (EDataCapture *data_capture)
+{
+       g_return_val_if_fail (E_IS_DATA_CAPTURE (data_capture), NULL);
+
+       return g_main_context_ref (data_capture->priv->main_context);
+}
+
diff --git a/e-util/e-data-capture.h b/e-util/e-data-capture.h
new file mode 100644
index 0000000..8949e67
--- /dev/null
+++ b/e-util/e-data-capture.h
@@ -0,0 +1,79 @@
+/*
+ * e-data-capture.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_DATA_CAPTURE_H
+#define E_DATA_CAPTURE_H
+
+#include <gio/gio.h>
+
+/* Standard GObject macros */
+#define E_TYPE_DATA_CAPTURE \
+       (e_data_capture_get_type ())
+#define E_DATA_CAPTURE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_DATA_CAPTURE, EDataCapture))
+#define E_DATA_CAPTURE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_DATA_CAPTURE, EDataCaptureClass))
+#define E_IS_DATA_CAPTURE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_DATA_CAPTURE))
+#define E_IS_DATA_CAPTURE_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_DATA_CAPTURE))
+#define E_DATA_CAPTURE_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_DATA_CAPTURE, EDataCaptureClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EDataCapture EDataCapture;
+typedef struct _EDataCaptureClass EDataCaptureClass;
+typedef struct _EDataCapturePrivate EDataCapturePrivate;
+
+/**
+ * EDataCapture:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _EDataCapture {
+       GObject parent;
+       EDataCapturePrivate *priv;
+};
+
+struct _EDataCaptureClass {
+       GObjectClass parent_class;
+
+       /* Signals */
+       void            (*finished)             (EDataCapture *capture,
+                                                GBytes *data);
+};
+
+GType          e_data_capture_get_type         (void) G_GNUC_CONST;
+EDataCapture * e_data_capture_new              (GMainContext *main_context);
+GMainContext * e_data_capture_ref_main_context (EDataCapture *data_capture);
+
+G_END_DECLS
+
+#endif /* E_DATA_CAPTURE_H */
+
diff --git a/e-util/e-util.h b/e-util/e-util.h
index d60a6f0..3870c45 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -85,6 +85,7 @@
 #include <e-util/e-client-selector.h>
 #include <e-util/e-config.h>
 #include <e-util/e-contact-store.h>
+#include <e-util/e-data-capture.h>
 #include <e-util/e-dateedit.h>
 #include <e-util/e-datetime-format.h>
 #include <e-util/e-destination-store.h>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]