[bijiben] VJOURNAL: initial support



commit 20b0c6dbb85b2b56835c9cb9551a679af7814b21
Author: Pierre-Yves Luyten <py luyten fr>
Date:   Tue Apr 22 00:38:15 2014 +0200

    VJOURNAL: initial support
    
    See #728581
    
    - VJOURNAL ("memos") notes from evolution accounts are
      automatically shown
    - They appear on the bottom (date is not handled)
      User can amend notes.
    - Categories is not supported yet
    - A crash occurs if an Ical is set as default storage
      and new notes is asked.
      Because it is not implemented.
    - Saving note does not damage attachments and so on

 configure.ac                               |    6 +-
 src/bjb-bijiben.c                          |   50 +++-
 src/libbiji/Makefile.am                    |    4 +
 src/libbiji/biji-manager.c                 |   16 +-
 src/libbiji/biji-manager.h                 |   13 +-
 src/libbiji/biji-note-obj.c                |    9 +-
 src/libbiji/biji-note-obj.h                |   33 ++
 src/libbiji/editor/biji-webkit-editor.c    |    2 +-
 src/libbiji/provider/biji-memo-note.c      |  432 ++++++++++++++++++++
 src/libbiji/provider/biji-memo-note.h      |   71 ++++
 src/libbiji/provider/biji-memo-provider.c  |  590 ++++++++++++++++++++++++++++
 src/libbiji/provider/biji-memo-provider.h  |   72 ++++
 src/libbiji/provider/biji-own-cloud-note.c |    2 +-
 13 files changed, 1293 insertions(+), 7 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 0e15a92..274d64e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,15 +58,19 @@ AM_PROG_LIBTOOL
 GLIB_REQUIRED_VERSION=2.28
 GTK_REQUIRED_VERSION=3.11.4
 
+
 PKG_CHECK_MODULES([TRACKER], [tracker-sparql-1.0], [sparql_version="tracker-sparql-1.0"],
    [PKG_CHECK_MODULES([TRACKER], [tracker-sparql-0.18], [sparql_version="tracker-sparql-0.18"])
 ])
 
 PKG_CHECK_MODULES(BIJIBEN,
-                  [ gio-unix-2.0
+                  [ evolution-calendar-3.0
+                   evolution-data-server-1.2
+                    gio-unix-2.0
                     glib-2.0 >= $GLIB_REQUIRED_VERSION
                     goa-1.0
                     gtk+-3.0 >= $GTK_REQUIRED_VERSION
+                   libecal-1.2
                     libxml-2.0
                     $sparql_version
                     uuid
diff --git a/src/bjb-bijiben.c b/src/bjb-bijiben.c
index 51a924a..8209bc2 100644
--- a/src/bjb-bijiben.c
+++ b/src/bjb-bijiben.c
@@ -20,6 +20,9 @@
 
 #include <glib/gi18n.h>
 #include <stdlib.h>
+#include <libedataserver/libedataserver.h> /* ESourceRegistry */
+#include <libecal/libecal.h>               /* ECalClient      */
+
 
 #include <libbiji/libbiji.h>
 
@@ -293,6 +296,50 @@ on_client_got (GObject *source_object,
   g_list_free (accounts);
 }
 
+
+
+
+
+/*
+ * Currently bjb checks eds accounts
+ * and asks libbiji about them.
+ * This, in case we want a setting
+ * Another way might be to let libbiji live its life
+ */
+void
+on_registry_got (GObject *obj,
+                 GAsyncResult *res,
+                 gpointer user_data)
+{
+  GError *error;
+  ESourceRegistry *registry;
+  GList *list, *l;
+  Bijiben *self = BIJIBEN_APPLICATION (user_data);
+
+  error = NULL;
+  registry = e_source_registry_new_finish (res, &error);
+
+  if (error)
+  {
+    g_warning ("no registry :(, %s", error->message);
+    return;
+  }
+
+  list = e_source_registry_list_sources (registry, E_SOURCE_EXTENSION_MEMO_LIST);
+  for (l=list; l!= NULL; l=l->next)
+  {
+    biji_manager_add_e_source_extension_memo (self->priv->manager, l->data);
+  }
+
+  g_list_free_full (list, g_object_unref);
+}
+
+
+
+
+
+
+
 static void
 bijiben_startup (GApplication *application)
 {
@@ -348,8 +395,9 @@ bijiben_startup (GApplication *application)
   if (error)
     goto out;
 
-  /* Goa */
+  /* Goa, e-d-s */
   goa_client_new  (NULL, on_client_got, self); // cancellable
+  e_source_registry_new (NULL, on_registry_got, self);
 
 
   /* Automatic imports on startup */
diff --git a/src/libbiji/Makefile.am b/src/libbiji/Makefile.am
index 80434f6..685bf82 100644
--- a/src/libbiji/Makefile.am
+++ b/src/libbiji/Makefile.am
@@ -72,6 +72,10 @@ libbiji_la_SOURCES =  \
        provider/biji-local-note.h \
        provider/biji-local-provider.c \
        provider/biji-local-provider.h \
+       provider/biji-memo-note.c \
+       provider/biji-memo-note.h \
+       provider/biji-memo-provider.c \
+       provider/biji-memo-provider.h \
        provider/biji-own-cloud-note.c \
        provider/biji-own-cloud-note.h \
        provider/biji-own-cloud-provider.c \
diff --git a/src/libbiji/biji-manager.c b/src/libbiji/biji-manager.c
index 76b4b62..e9d76d1 100644
--- a/src/libbiji/biji-manager.c
+++ b/src/libbiji/biji-manager.c
@@ -24,6 +24,7 @@
 
 #include "provider/biji-import-provider.h"
 #include "provider/biji-local-provider.h"
+#include "provider/biji-memo-provider.h"
 #include "provider/biji-own-cloud-provider.h"
 
 
@@ -525,11 +526,24 @@ _add_provider (BijiManager *self,
 
   info = biji_provider_get_info (provider);
   g_hash_table_insert (self->priv->providers, (gpointer) info->unique_id, provider);
-  g_signal_connect (provider, "loaded", 
+  g_signal_connect (provider, "loaded",
                     G_CALLBACK (on_provider_loaded_cb), self);
 }
 
 
+
+
+void
+biji_manager_add_e_source_extension_memo (BijiManager *self,
+                                          ESource *source)
+{
+  BijiProvider *provider = NULL;
+
+  provider = biji_memo_provider_new (self, source);
+  _add_provider (self, provider);
+}
+
+
 void
 biji_manager_add_goa_object (BijiManager *self,
                                GoaObject *object)
diff --git a/src/libbiji/biji-manager.h b/src/libbiji/biji-manager.h
index 164671c..ac23b2b 100644
--- a/src/libbiji/biji-manager.h
+++ b/src/libbiji/biji-manager.h
@@ -11,9 +11,14 @@
 #include "biji-info-set.h"
 #include "biji-note-obj.h"
 
+
+#include <libedataserver/libedataserver.h> /* ESourceRegistry */
+
 #define GOA_API_IS_SUBJECT_TO_CHANGE
 #include <goa/goa.h>
 
+
+
 G_BEGIN_DECLS
 
 /* The flag tells if view should reload the whole model or not */
@@ -62,7 +67,7 @@ struct _BijiManager
 
 
 
-GType biji_manager_get_type (void) G_GNUC_CONST; 
+GType biji_manager_get_type (void) G_GNUC_CONST;
 
 
 
@@ -76,6 +81,12 @@ void             biji_manager_import_uri            (BijiManager *manager,
                                                      gchar *uri);
 
 
+
+void             biji_manager_add_e_source_extension_memo
+                                                    (BijiManager *self,
+                                                     ESource *source);
+
+
 void             biji_manager_add_goa_object        (BijiManager *manager,
                                                      GoaObject *object);
 
diff --git a/src/libbiji/biji-note-obj.c b/src/libbiji/biji-note-obj.c
index 5aa6f42..c4c8733 100644
--- a/src/libbiji/biji-note-obj.c
+++ b/src/libbiji/biji-note-obj.c
@@ -825,7 +825,14 @@ html_from_plain_text                        (gchar *content)
                                 NULL);
 
   retval = g_strconcat ("<html xmlns=\"http://www.w3.org/1999/xhtml\";>",
-                        "<body>", escaped, "</body></html>", NULL);
+                        "<body contenteditable='true' id='editable'>",
+                        "<script type='text/javascript'>",
+                        "    window.onload = function () {",
+                        "      document.getElementById('editable').focus();",
+                        "    };",
+                        "</script>",
+                        escaped,
+                        "</body></html>", NULL);
 
   g_free (escaped);
   return retval;
diff --git a/src/libbiji/biji-note-obj.h b/src/libbiji/biji-note-obj.h
index 6df6059..03010b2 100644
--- a/src/libbiji/biji-note-obj.h
+++ b/src/libbiji/biji-note-obj.h
@@ -57,14 +57,47 @@ struct _BijiNoteObjClass
   BijiItemClass parent_class;
 
   gchar*        (*get_basename)      (BijiNoteObj *note);
+
+  /*
+   * Mandatory. Provide the latest note html.
+   * Use html_from_plain_text if needed. */
   gchar*        (*get_html)          (BijiNoteObj *note);
+
+  /*
+   * Mandatory. When editor amends html, assign it */
   void          (*set_html)          (BijiNoteObj *note, gchar *html);
+
+  /*
+   * Mandatory. Store the note. This might be async. */
   void          (*save_note)         (BijiNoteObj *note);
+
+  /*
+   * Mandatory
+   * Move the note to trash bin. What to do depends on the provider
+   * Return FALSE is this fails.
+   * If the provider does not support trash,
+   * you should delete the note */
   gboolean      (*archive)           (BijiNoteObj *note);
+
+  /*
+   * Mandatory
+   * Return TRUE if the note is marked as trashed in any way
+   * (depending on the provider)
+   *
+   * Return FALSE if the note is note trashed.
+   *
+   * If the provider does not suport this, return FALSE */
   gboolean      (*is_trashed)        (BijiNoteObj *note);
+
+  /*
+   * Mandatory
+   * Return TRUE if the note can store rich text:
+   * - bold, italic, srike
+   */
   gboolean      (*can_format)        (BijiNoteObj *note);
 };
 
+
 struct _BijiNoteObj
 {
   BijiItem parent_instance;
diff --git a/src/libbiji/editor/biji-webkit-editor.c b/src/libbiji/editor/biji-webkit-editor.c
index 50487ed..7718107 100644
--- a/src/libbiji/editor/biji-webkit-editor.c
+++ b/src/libbiji/editor/biji-webkit-editor.c
@@ -274,7 +274,7 @@ on_content_changed (WebKitWebView *view)
   {
     gchar *title;
     gchar *unique_title;
-  
+
     title = rows[0];
 
     if (g_strcmp0 (title, biji_item_get_title (BIJI_ITEM (note))) != 0)
diff --git a/src/libbiji/provider/biji-memo-note.c b/src/libbiji/provider/biji-memo-note.c
new file mode 100644
index 0000000..dac17b5
--- /dev/null
+++ b/src/libbiji/provider/biji-memo-note.c
@@ -0,0 +1,432 @@
+/* bjb-memo-note.c
+ * Copyright (C) Pierre-Yves LUYTEN 2014 <py luyten fr>
+ *
+ * bijiben 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * bijiben 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+#include "biji-memo-provider.h"
+#include "biji-memo-note.h"
+
+struct _BijiMemoNotePrivate
+{
+  BijiProvider  *provider;
+  ECalComponent *ecal;
+  ECalClient    *client;
+  gchar         *description;
+  BijiNoteID    *id;
+};
+
+
+G_DEFINE_TYPE (BijiMemoNote, biji_memo_note, BIJI_TYPE_NOTE_OBJ);
+
+
+/* Properties */
+enum {
+  PROP_0,
+  PROP_ECAL,
+  MEMO_NOTE_PROP
+};
+
+
+static GParamSpec *properties[MEMO_NOTE_PROP] = { NULL, };
+
+
+
+/* Function from evo calendar gui comp-util.c (LGPL)
+ * to be removed if we depen on evo,
+ * the func is borrowed as is (=3.13.1) */
+gboolean
+cal_comp_is_on_server (ECalComponent *comp,
+                       ECalClient *client)
+{
+  const gchar *uid;
+  gchar *rid = NULL;
+  icalcomponent *icalcomp = NULL;
+  GError *error = NULL;
+
+  g_return_val_if_fail (comp != NULL, FALSE);
+  g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
+  g_return_val_if_fail (client != NULL, FALSE);
+  g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+
+  /* See if the component is on the server.  If it is not, then it likely
+   * means that the appointment is new, only in the day view, and we
+   * haven't added it yet to the server.       In that case, we don't need to
+   * confirm and we can just delete the event.  Otherwise, we ask
+   * the user.
+   */
+  e_cal_component_get_uid (comp, &uid);
+
+  /* TODO We should not be checking for this here. But since
+   *   e_cal_util_construct_instance does not create the instances
+   *   of all day events, so we default to old behaviour. */
+  if (e_cal_client_check_recurrences_no_master (client))
+  {
+    rid = e_cal_component_get_recurid_as_string (comp);
+  }
+
+  e_cal_client_get_object_sync (
+       client, uid, rid, &icalcomp, NULL, &error);
+
+  if (icalcomp != NULL)
+  {
+    icalcomponent_free (icalcomp);
+    g_free (rid);
+
+    return TRUE;
+  }
+
+  if (!g_error_matches (error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND))
+    g_warning (G_STRLOC ": %s", error->message);
+
+  g_clear_error (&error);
+  g_free (rid);
+
+  return FALSE;
+}
+
+
+
+/*
+ * Parse current note content to update ECalComponent
+ *
+ * "clone" properties we do not change remain as "comp"
+ * This is true for attachments, too.
+ *
+ * Comments refer to where evolution 3.13.1 does the same.
+ */
+static void
+fill_in_components (ECalComponent *comp,
+                    ECalComponent *clone,
+                   BijiMemoNote  *self)
+{
+  ECalComponentText  text;
+  GSList             l;
+
+
+  /* ----------------- FIELDS FROM "memo_page_fill_components"------------------ */
+
+
+  /* Set : title */
+  text.value = biji_item_get_title (BIJI_ITEM (self));
+  text.altrep = NULL;
+  e_cal_component_set_summary (clone, &text);
+
+  /* Set : content */
+  text.value = biji_note_obj_get_raw_text (BIJI_NOTE_OBJ (self));
+  text.altrep = NULL;
+  l.data = &text;
+  l.next = NULL;
+  e_cal_component_set_description_list (clone, &l);
+
+
+  /* dtstart : we'd rather use "created", "modified" */
+
+  /* Classification :? */
+
+
+  /* Categories : to be implemented */
+
+  /* Recipients : do not touch this */
+  /* Organizer  : do not touch this */
+
+
+  /* -------------  FIELDS FROM "save_comp" in comp-editor.c ------- */
+
+  /* Attachment list */
+
+
+  /* rdate, rrule, exdate, exrule */
+
+  /* Sequence */
+
+
+  /* X-EVOLUTION-OPTIONS-DELAY */
+
+
+  /* -------------- OHER FILEDS (from specification) -------
+   *    FIXME: some of theme belong somewhere above....
+   *
+   * Unique: created / description / dtstamp / last-mod / recurid / status / url /
+   * Several: attach / attendee / comment / contact / related / rstatus / x-prop
+  */
+}
+
+
+
+/*
+ * https://git.gnome.org/browse/evolution/tree/calendar/gui/dialogs/comp-editor.c#n471
+ */
+static void
+memo_note_save (BijiNoteObj *note)
+{
+  BijiMemoNote *self = BIJI_MEMO_NOTE (note);
+  BijiMemoNotePrivate *priv = self->priv;
+  const gchar *orig_uid;
+  icalcomponent *icalcomp;
+  gboolean result;
+  GError *error;
+  ECalComponent *clone;
+  //gchar *orig_uid_copy;
+
+  clone = e_cal_component_clone (priv->ecal);
+  fill_in_components (priv->ecal, clone, self);
+
+  /* Save */
+  e_cal_component_commit_sequence (clone);
+  g_object_unref (priv->ecal);
+  priv->ecal = clone;
+
+  e_cal_component_get_uid (priv->ecal, &orig_uid);
+
+  /* Make a copy of it, because call of e_cal_create_object()
+   * rewrites the internal uid.
+   orig_uid_copy = g_strdup (orig_uid); */
+  icalcomp = e_cal_component_get_icalcomponent (priv->ecal);
+
+
+  if (!cal_comp_is_on_server (priv->ecal, priv->client))
+  {
+    gchar *uid = NULL;
+    result = e_cal_client_create_object_sync (
+    priv->client, icalcomp, &uid, NULL, &error);
+      if (result)
+      {
+        icalcomponent_set_uid (icalcomp, uid);
+       g_free (uid);
+        //g_signal_emit_by_name (editor, "object_created");
+      }
+   }
+
+  else
+  {
+    result = e_cal_client_modify_object_sync (
+      priv->client, icalcomp, E_CAL_OBJ_MOD_THIS, NULL, &error);
+    e_cal_component_commit_sequence (clone);
+  }
+}
+
+
+/* Save title when saving note.
+ * No need to do anything here */
+static void
+on_title_changed_cb (BijiMemoNote *self)
+{
+}
+
+
+
+static void
+biji_memo_note_constructed (GObject *obj)
+{
+  BijiMemoNote *self = BIJI_MEMO_NOTE (obj);
+
+  G_OBJECT_CLASS (biji_memo_note_parent_class)->constructed (obj);
+
+  g_signal_connect_swapped (self->priv->id, "notify::title",
+                            G_CALLBACK (on_title_changed_cb), self);
+}
+
+
+static void
+biji_memo_note_init (BijiMemoNote *biji_memo_note)
+{
+  biji_memo_note->priv = G_TYPE_INSTANCE_GET_PRIVATE (biji_memo_note, BIJI_TYPE_MEMO_NOTE, 
BijiMemoNotePrivate);
+
+}
+
+
+
+/* Let the provider finalize the ECalComponent. */
+static void
+biji_memo_note_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (biji_memo_note_parent_class)->finalize (object);
+}
+
+
+
+
+static void
+biji_memo_note_set_property (GObject      *object,
+                             guint         property_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  BijiMemoNote *self = BIJI_MEMO_NOTE (object);
+
+
+  switch (property_id)
+    {
+    case PROP_ECAL:
+      self->priv->ecal = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+
+static void
+biji_memo_note_get_property (GObject    *object,
+                             guint       property_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  BijiMemoNote *self = BIJI_MEMO_NOTE (object);
+
+  switch (property_id)
+    {
+    case PROP_ECAL:
+      g_value_set_object (value, self->priv->ecal);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+
+
+static void
+memo_set_html (BijiNoteObj *note, gchar *html)
+{
+  /* NULL */
+}
+
+
+
+static gboolean
+memo_delete (BijiNoteObj *note)
+{
+  g_warning ("delete is not implemented yet");
+
+  return FALSE;
+}
+
+
+static gchar *
+memo_get_html (BijiNoteObj *note)
+{
+  // we cast but the func should expect a const gchar, really
+  return html_from_plain_text ((gchar*) biji_note_obj_get_raw_text (note));
+}
+
+
+
+static const gchar *
+memo_get_place (BijiItem *item)
+{
+  BijiMemoNote *self;
+  const BijiProviderInfo *info;
+
+  self = BIJI_MEMO_NOTE (item);
+  info = biji_provider_get_info (BIJI_PROVIDER (self->priv->provider));
+
+  return info->name;
+}
+
+
+
+static gboolean
+item_no         (BijiItem * item)
+{
+  return FALSE;
+}
+
+
+static gboolean
+note_no         (BijiNoteObj *item)
+{
+  return FALSE;
+}
+
+
+static void
+biji_memo_note_class_init (BijiMemoNoteClass *klass)
+{
+  GObjectClass     *object_class = G_OBJECT_CLASS (klass);
+  BijiItemClass    *item_class;
+  BijiNoteObjClass *note_class;
+
+  item_class = BIJI_ITEM_CLASS (klass);
+  note_class = BIJI_NOTE_OBJ_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (BijiMemoNotePrivate));
+
+  object_class->finalize = biji_memo_note_finalize;
+  object_class->constructed = biji_memo_note_constructed;
+  object_class->get_property = biji_memo_note_get_property;
+  object_class->set_property = biji_memo_note_set_property;
+
+  item_class->is_collectable = item_no;
+  item_class->has_color = item_no;
+  item_class->get_place = memo_get_place;
+
+  note_class->get_basename = NULL;
+  note_class->get_html = memo_get_html;
+  note_class->set_html = memo_set_html;
+  note_class->save_note = memo_note_save;
+  note_class->can_format = note_no;
+  note_class->archive = memo_delete;
+  note_class->is_trashed = note_no;
+
+  properties[PROP_ECAL] =
+    g_param_spec_object("ecal",
+                        "ECalComponent",
+                        "ECalComponent which this note refers to",
+                        E_TYPE_CAL_COMPONENT,
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, MEMO_NOTE_PROP, properties);
+}
+
+
+
+
+
+
+/*
+ * The provider looks for a content ("description")
+ * in order to push to tracker.
+ *
+ */
+BijiNoteObj *
+biji_memo_note_new_from_info          (BijiMemoProvider *provider,
+                                       BijiManager      *manager,
+                                       BijiInfoSet      *info,
+                                      ECalComponent    *component,
+                                       gchar            *description,
+                                      ECalClient       *client)
+{
+  BijiNoteID *id;
+  BijiMemoNote *ret;
+
+  id = biji_note_id_new_from_info (info);
+
+  ret = g_object_new (BIJI_TYPE_MEMO_NOTE,
+                       "manager", manager,
+                       "id", id,
+                      "ecal", component,
+                       NULL);
+
+  ret->priv->id = id;
+  ret->priv->provider = BIJI_PROVIDER (provider);
+  ret->priv->description = description;
+  ret->priv->client = client;
+  return BIJI_NOTE_OBJ (ret);
+}
diff --git a/src/libbiji/provider/biji-memo-note.h b/src/libbiji/provider/biji-memo-note.h
new file mode 100644
index 0000000..3055e2e
--- /dev/null
+++ b/src/libbiji/provider/biji-memo-note.h
@@ -0,0 +1,71 @@
+/* bjb-memo-note.h
+ * Copyright (C) Pierre-Yves LUYTEN 2014 <py luyten fr>
+ *
+ * bijiben 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * bijiben 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _BIJI_MEMO_NOTE_H_
+#define _BIJI_MEMO_NOTE_H_ 1
+
+#include <libecal/libecal.h>               /* ECalClient      */
+
+#include "biji-tracker.h"
+#include "biji-note-id.h"
+#include "biji-note-obj.h"
+
+
+G_BEGIN_DECLS
+
+#define BIJI_TYPE_MEMO_NOTE             (biji_memo_note_get_type ())
+#define BIJI_MEMO_NOTE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), BIJI_TYPE_MEMO_NOTE, 
BijiMemoNote))
+#define BIJI_MEMO_NOTE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), BIJI_TYPE_MEMO_NOTE, 
BijiMemoNoteClass))
+#define BIJI_IS_MEMO_NOTE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BIJI_TYPE_MEMO_NOTE))
+#define BIJI_IS_MEMO_NOTE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), BIJI_TYPE_MEMO_NOTE))
+#define BIJI_MEMO_NOTE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), BIJI_TYPE_MEMO_NOTE, 
BijiMemoNoteClass))
+
+typedef struct _BijiMemoNoteClass BijiMemoNoteClass;
+typedef struct _BijiMemoNote BijiMemoNote;
+typedef struct _BijiMemoNotePrivate BijiMemoNotePrivate;
+
+
+struct _BijiMemoNote
+{
+    BijiNoteObj parent_instance;
+
+    BijiMemoNotePrivate *priv;
+};
+
+
+struct _BijiMemoNoteClass
+{
+    BijiNoteObjClass parent_class;
+};
+
+
+
+GType                  biji_memo_note_get_type               (void) G_GNUC_CONST;
+
+
+BijiNoteObj           *biji_memo_note_new_from_info          (BijiMemoProvider *provider,
+                                                              BijiManager      *manager,
+                                                              BijiInfoSet      *info,
+                                                             ECalComponent    *comp,
+                                                             gchar            *description,
+                                                              ECalClient       *client);
+
+
+G_END_DECLS
+
+#endif /* _BIJI_MEMO_NOTE_H_ */
+
diff --git a/src/libbiji/provider/biji-memo-provider.c b/src/libbiji/provider/biji-memo-provider.c
new file mode 100644
index 0000000..ddc2190
--- /dev/null
+++ b/src/libbiji/provider/biji-memo-provider.c
@@ -0,0 +1,590 @@
+/*
+ * biji-memo-provider.c
+ * Copyright (C) Pierre-Yves LUYTEN 2013 <py luyten fr>
+ *
+ * bijiben 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * bijiben 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * http://tools.ietf.org/html/rfc2445
+ * 
+ * Evolution UI offers to sync Memo to local computer
+ * TODO: check this
+ */
+
+
+#include <libecal/libecal.h>        /* ECalClient      */
+
+#include "biji-memo-provider.h"
+#include "biji-memo-note.h"
+
+
+#define MINER_ID "gn:memo:miner:fd48e15b-2460-4761-a7be-942616102fa6"
+
+struct _BijiMemoProviderPrivate
+{
+  BijiProviderInfo     info;
+  ESource             *source;
+  ECalClient          *client;
+
+  /* Startup */
+  GSList              *memos;
+  GHashTable          *tracker;
+  GHashTable          *items;
+  GHashTable          *archives;
+  GQueue              *queue;
+};
+
+
+
+
+G_DEFINE_TYPE (BijiMemoProvider, biji_memo_provider, BIJI_TYPE_PROVIDER);
+
+
+/* Properties */
+enum {
+  PROP_0,
+  PROP_SOURCE,
+  BIJI_MEMO_PROP
+};
+
+
+
+static GParamSpec *properties[BIJI_MEMO_PROP] = { NULL, };
+
+/* Memos */
+
+
+typedef struct
+{
+  ECalComponent     *ecal;
+  BijiMemoProvider  *self;
+  BijiInfoSet        set;
+} BijiMemoItem;
+
+
+
+static BijiMemoItem *
+memo_item_new (BijiMemoProvider *self)
+{
+  BijiMemoItem *item;
+
+  item = g_slice_new (BijiMemoItem);
+  item->ecal = NULL;
+  item->self = self;
+
+  item->set.content = NULL;
+  item->set.mtime = 0;
+  item->set.created = 0;
+  item->set.title = NULL;
+  item->set.url = NULL;
+
+  return item;
+}
+
+
+/* ECalComponent do not transfer much values */
+static void
+memo_item_free (BijiMemoItem *item)
+{
+  g_free (item->set.content);
+  g_slice_free (BijiMemoItem, item);
+}
+
+
+
+/* No color, hence we use default color */
+
+static void
+create_note_from_item (BijiMemoItem *item)
+{
+  BijiNoteObj *note;
+  GdkRGBA color;
+  BijiManager *manager;
+
+  manager = biji_provider_get_manager (BIJI_PROVIDER (item->self));
+  note = biji_memo_note_new_from_info (item->self,
+                                       manager,
+                                       &item->set,
+                                      item->ecal,
+                                       item->set.content,
+                                       item->self->priv->client);
+
+  biji_manager_get_default_color (manager, &color);
+  biji_note_obj_set_rgba (note, &color);
+  g_hash_table_replace (item->self->priv->items,
+                        item->set.url,
+                        note);
+}
+
+
+
+static void
+trash (gpointer urn_uuid, gpointer self)
+{
+  biji_tracker_trash_ressource (
+      biji_provider_get_manager (BIJI_PROVIDER (self)), (gchar*) urn_uuid);
+}
+
+
+static void
+handle_next_item (BijiMemoProvider *self)
+{
+  BijiMemoProviderPrivate *priv = self->priv;
+  BijiMemoItem *item;
+  GList        *list;
+
+  item = g_queue_pop_head (self->priv->queue);
+
+  if (item != NULL)
+  {
+    g_hash_table_remove (priv->tracker, item->set.url);
+
+    create_note_from_item (item);
+    /* debug pour tracker. Il faut en plus datasource->urn */
+    g_debug ("created=%li", item->set.created);
+    g_debug ("title=%s", item->set.title);
+    g_debug ("url=%s", item->set.url);
+    g_debug ("content=%s\n================\n\n\n", item->set.content);
+
+    biji_tracker_ensure_ressource_from_info (
+    biji_provider_get_manager (BIJI_PROVIDER (self)), &item->set);
+
+    //memo_item_free (item);
+    handle_next_item (self);
+  }
+
+
+  /* Loading and notes creation over. Provide notes to manager */
+  else
+  {
+    /* Post load tracker db clean-up */
+    list = g_hash_table_get_values (self->priv->tracker);
+    g_list_foreach (list, trash, self);
+    g_list_free (list);
+
+    /* Now simply provide data to controller */
+    list = g_hash_table_get_values (self->priv->items);
+    BIJI_PROVIDER_GET_CLASS (self)->notify_loaded (BIJI_PROVIDER (self), list, BIJI_LIVING_ITEMS);
+    g_list_free (list);
+  }
+}
+
+
+
+static void
+on_object_list_got (GObject      *obj,
+                    GAsyncResult *res,
+                    gpointer      user_data)
+{
+  GSList *l, *desc, *ll;
+  GError *error;
+  BijiMemoProvider *self = BIJI_MEMO_PROVIDER (user_data);
+
+  error = NULL;
+  e_cal_client_get_object_list_finish (self->priv->client,
+                                       res,
+                                       &self->priv->memos,
+                                       &error);
+
+  if (error)
+  {
+    g_warning ("e cal get obj list fin %s", error->message);
+    g_error_free (error);
+    return;
+  }
+
+/*
+ * ECALCOMPONENT
+ * uid, categories, created (or dtstart), last modified,
+ * summary,
+ *
+ * but the tricky part is description,
+ * since ical says as-many-descriptions-as-you-want
+ * while we need a single content.
+ * Let's start with something simple: take the last
+ * description we find.
+ *
+ * e_cal_component_free_******
+ *
+ */
+
+
+/*
+
+void                e_cal_client_get_timezone           (ECalClient *client,
+                                                         const gchar *tzid,
+                                                         GCancellable *cancellable,
+                                                         GAsyncReadyCallback callback,
+                                                         gpointer user_data);
+*/
+
+  for (l=self->priv->memos; l!=NULL; l=l->next)
+  {
+    ECalComponent      *co; /* Memo */
+    ECalComponentText   text;
+    const gchar        *uid;
+    BijiMemoItem       *item;
+    icaltimetype       *t;
+    gchar              *iso;
+    //GTimeVal            time = {0;0};
+    //gint64              sec;
+
+    item = memo_item_new (self);
+    item->set.datasource_urn = g_strdup (self->priv->info.datasource);
+    co = item->ecal = e_cal_component_new_from_icalcomponent (l->data);
+
+#ifdef FALSE
+//iso8601 => g_time_val.tv_sec
+struct icaltimetype
+{
+int year;      /**< Actual year, e.g. 2001. */
+int month;     /**< 1 (Jan) to 12 (Dec). */
+int day;
+int hour;
+int minute;
+int second;
+int is_utc; /**< 1-> time is in UTC timezone */
+int is_date; /**< 1 -> interpret this as date. */
+int is_daylight; /**< 1 -> time is in daylight savings time. */
+const icaltimezone *zone;      /**< timezone */
+};
+#endif
+
+    e_cal_component_get_summary (co, &text);
+    item->set.title = g_strdup (text.value);
+    e_cal_component_get_uid (co, &uid);
+    item->set.url = g_strdup (uid);
+
+    /* Set url contains timezone. Not the-right-thing-to-do however */
+
+    // time: we expect something like time.tv_sec (sec since)
+    e_cal_component_get_last_modified (co, &t); // or dtstart
+//gchar *             isodate_from_time_t                 (time_t t);
+
+    iso = g_strdup_printf  ("%i-%i-%iT%i:%i:%i",
+                            t->year,
+                            t->month,
+                           t->day,
+                           t->hour,
+                           t->minute,
+                           t->second);
+    g_warning ("iso=%s", iso);
+
+    item->set.mtime = 0;
+    e_cal_component_free_icaltimetype (t);
+    e_cal_component_get_created (co, &t); // or dtstart
+    item->set.created = 0;
+    e_cal_component_free_icaltimetype (t);
+
+    e_cal_component_get_description_list (co, &desc);
+    for (ll=desc; ll!=NULL; ll=ll->next)
+    {
+      ECalComponentText *txt = ll->data;
+
+      if (txt->value != NULL)
+      {
+        //g_warning ("txt value got: memo %s has\n %s\n====", item->set.title, txt->value);
+        item->set.content = g_strdup (txt->value);
+       break;
+      }
+    }
+
+    if (item->set.content == NULL)
+      g_warning ("note %s has no content", item->set.title);
+
+
+    e_cal_component_free_text_list (desc);
+    g_queue_push_head (self->priv->queue, item);
+  }
+
+  handle_next_item (self);
+}
+
+
+
+
+static gchar *
+i_want_all_memos (void)
+{
+  g_warning ("FIXME: memo provider query is wrong");
+/*
+  return "occur-in-time-range? "
+         "(make-time \"19000101T204153Z\") "
+         "(make-time \"22220202T204153Z\")";*/
+  return "occur-in-time-range? (make-time \"20110421T204153Z\") (make-time \"20140423T232416Z\")";
+}
+
+
+
+/* Stock all existing urn-uuid. Then parse files */
+static void
+on_notes_mined (GObject       *source_object,
+                GAsyncResult  *res,
+                gpointer       user_data)
+{
+  BijiMemoProvider        *self;
+  TrackerSparqlConnection *connect;
+  TrackerSparqlCursor     *cursor;
+  GError                  *error;
+
+  self = user_data;
+  connect = TRACKER_SPARQL_CONNECTION (source_object);
+  error = NULL;
+  cursor = tracker_sparql_connection_query_finish (connect, res, &error);
+
+  if (error)
+  {
+    g_warning ("%s", error->message);
+    g_error_free (error);
+  }
+
+  if (cursor)
+  {
+    while (tracker_sparql_cursor_next (cursor, NULL, NULL))
+    {
+      g_hash_table_insert (self->priv->tracker,
+                           g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL)),
+                           g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL)));
+
+    }
+  }
+
+  e_cal_client_get_object_list (self->priv->client,
+                                i_want_all_memos(), /* sexp, not null */
+                                NULL, /* Cancellable */
+                                on_object_list_got,
+                                self);
+}
+
+
+/*
+ * Once the client is connected,
+ * mine notes for this.
+ */
+static void
+on_client_connected (GObject      *obj,
+                     GAsyncResult *res,
+                     gpointer      user_data)
+{
+  GError *error;
+  error = NULL;
+  gchar *query;
+  BijiMemoProvider *self = BIJI_MEMO_PROVIDER (user_data);
+
+  e_cal_client_connect_finish (res, &error);
+
+  if (error)
+  {
+      g_warning ("On Client Connected : %s", error->message);
+      return;
+  }
+
+  self->priv->client = E_CAL_CLIENT (obj);
+
+  g_warning ("datasource=%s", self->priv->info.datasource);
+  query = g_strdup_printf ("SELECT ?url ?urn WHERE {?urn a nfo:Note; "
+                           " nie:dataSource '%s' ; nie:url ?url}",
+                           self->priv->info.datasource);
+
+  tracker_sparql_connection_query_async (
+      biji_manager_get_tracker_connection (
+        biji_provider_get_manager (BIJI_PROVIDER (self))),
+      query,
+      NULL,
+      on_notes_mined,
+      self);
+
+  g_free (query);
+}
+
+
+/* GObject */
+
+
+/*
+ * TODO : implement the icon. At least some color
+
+hint : Default value: "#becedd" > this is hex, AFAIM we want GdkRGBA
+const gchar *       e_source_selectable_get_color       (ESourceSelectable *extension);
+
+  GObject
+   +----ESourceExtension
+         +----ESourceBackend
+               +----ESourceSelectable
+                     +----ESourceCalendar
+                     +----ESourceMemoList
+                     +----ESourceTaskList
+
+*/
+
+static void
+biji_memo_provider_constructed (GObject *obj)
+{
+  BijiMemoProvider        *self;
+  BijiMemoProviderPrivate *priv;
+
+  G_OBJECT_CLASS (biji_memo_provider_parent_class)->constructed (obj);
+
+  self = BIJI_MEMO_PROVIDER (obj);
+  priv = self->priv;
+
+  /* Info */
+  priv->info.unique_id = e_source_get_uid (priv->source);
+  priv->info.datasource = g_strdup_printf ("memo:%s",
+                                           priv->info.unique_id);
+  priv->info.name = g_strdup (e_source_get_display_name (priv->source));
+  priv->info.icon =
+    gtk_image_new_from_icon_name ("user-home", GTK_ICON_SIZE_INVALID);
+  gtk_image_set_pixel_size (GTK_IMAGE (priv->info.icon), 48);
+  g_object_ref (priv->info.icon);
+
+  e_cal_client_connect (self->priv->source,
+                        E_CAL_CLIENT_SOURCE_TYPE_MEMOS,
+                        NULL, /* cancel */
+                        on_client_connected,
+                        self);
+}
+
+
+
+/*
+
+
+void                e_cal_client_create_object          (ECalClient *client,
+                                                         icalcomponent *icalcomp,
+                                                         GCancellable *cancellable,
+                                                         GAsyncReadyCallback callback,
+                                                         gpointer user_data);
+gboolean            e_cal_client_create_object_finish   (ECalClient *client,
+                                                         GAsyncResult *result,
+                                                         gchar **out_uid,
+                                                         GError **error);
+
+*/
+
+
+
+static void
+biji_memo_provider_init (BijiMemoProvider *self)
+{
+  BijiMemoProviderPrivate *priv;
+
+  priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, BIJI_TYPE_MEMO_PROVIDER, BijiMemoProviderPrivate);
+
+  priv->queue = g_queue_new ();
+  priv->items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+  priv->tracker = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+  priv->memos = NULL;
+
+}
+
+static void
+biji_memo_provider_finalize (GObject *object)
+{
+  e_cal_client_free_icalcomp_slist (BIJI_MEMO_PROVIDER (object)->priv->memos);
+
+  G_OBJECT_CLASS (biji_memo_provider_parent_class)->finalize (object);
+}
+
+
+
+static void
+biji_memo_provider_set_property (GObject      *object,
+                                 guint         property_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  BijiMemoProvider *self = BIJI_MEMO_PROVIDER (object);
+
+
+  switch (property_id)
+    {
+    case PROP_SOURCE:
+      self->priv->source = g_value_get_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+
+static void
+biji_memo_provider_get_property (GObject    *object,
+                                 guint       property_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  BijiMemoProvider *self = BIJI_MEMO_PROVIDER (object);
+
+  switch (property_id)
+    {
+    case PROP_SOURCE:
+      g_value_set_object (value, self->priv->source);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+
+
+const BijiProviderInfo *
+memo_provider_get_info (BijiProvider *provider)
+{
+  return &(BIJI_MEMO_PROVIDER (provider)->priv->info);
+}
+
+
+static void
+biji_memo_provider_class_init (BijiMemoProviderClass *klass)
+{
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+  BijiProviderClass *provider_class = BIJI_PROVIDER_CLASS (klass);
+
+  object_class->finalize = biji_memo_provider_finalize;
+  object_class->constructed = biji_memo_provider_constructed;
+  object_class->get_property = biji_memo_provider_get_property;
+  object_class->set_property = biji_memo_provider_set_property;
+
+  provider_class->get_info = memo_provider_get_info;
+  provider_class->create_new_note = NULL;
+  provider_class->create_note_full = NULL;
+
+  properties[PROP_SOURCE] =
+    g_param_spec_object ("source",
+                         "Provider ESource",
+                         "ESource Memo associated to Provider",
+                         E_TYPE_SOURCE,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, BIJI_MEMO_PROP, properties);
+
+  g_type_class_add_private (klass, sizeof (BijiMemoProviderPrivate));
+}
+
+
+
+BijiProvider *
+biji_memo_provider_new (BijiManager *manager,
+                        ESource     *source)
+{
+    return g_object_new (BIJI_TYPE_MEMO_PROVIDER,
+                         "manager", manager,
+                         "source",  source,
+                         NULL);
+}
diff --git a/src/libbiji/provider/biji-memo-provider.h b/src/libbiji/provider/biji-memo-provider.h
new file mode 100644
index 0000000..e9e9f49
--- /dev/null
+++ b/src/libbiji/provider/biji-memo-provider.h
@@ -0,0 +1,72 @@
+/*
+ * biji-memo-provider.h
+ * Copyright (C) Pierre-Yves LUYTEN 2014 <py luyten fr>
+ * 
+ * bijiben 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 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * bijiben 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _BIJI_MEMO_PROVIDER_H_
+#define _BIJI_MEMO_PROVIDER_H_ 1
+
+
+#include <libedataserver/libedataserver.h> /* ESourceRegistry */
+//#include <libecal/libecal.h>               /* ECalClient      */
+
+#include "../biji-manager.h"
+#include "biji-provider.h"
+
+
+
+G_BEGIN_DECLS
+
+#define BIJI_TYPE_MEMO_PROVIDER             (biji_memo_provider_get_type ())
+#define BIJI_MEMO_PROVIDER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), BIJI_TYPE_MEMO_PROVIDER, 
BijiMemoProvider))
+#define BIJI_MEMO_PROVIDER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), BIJI_TYPE_MEMO_PROVIDER, 
BijiMemoProviderClass))
+#define BIJI_IS_MEMO_PROVIDER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BIJI_TYPE_MEMO_PROVIDER))
+#define BIJI_IS_MEMO_PROVIDER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), BIJI_TYPE_MEMO_PROVIDER))
+#define BIJI_MEMO_PROVIDER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), BIJI_TYPE_MEMO_PROVIDER, 
BijiMemoProviderClass))
+
+typedef struct _BijiMemoProviderClass BijiMemoProviderClass;
+typedef struct _BijiMemoProvider BijiMemoProvider;
+typedef struct _BijiMemoProviderPrivate BijiMemoProviderPrivate;
+
+
+
+
+struct _BijiMemoProvider
+{
+  BijiProvider             parent_instance;
+  BijiMemoProviderPrivate *priv;
+};
+
+
+
+struct _BijiMemoProviderClass
+{
+  BijiProviderClass parent_class;
+};
+
+
+
+GType           biji_memo_provider_get_type     (void) G_GNUC_CONST;
+
+
+BijiProvider   *biji_memo_provider_new          (BijiManager *manager,
+                                                 ESource     *source);
+
+
+G_END_DECLS
+
+#endif /* _BIJI_MEMO_PROVIDER_H_ */
+
diff --git a/src/libbiji/provider/biji-own-cloud-note.c b/src/libbiji/provider/biji-own-cloud-note.c
index 0eb5d04..2491961 100644
--- a/src/libbiji/provider/biji-own-cloud-note.c
+++ b/src/libbiji/provider/biji-own-cloud-note.c
@@ -403,7 +403,7 @@ BijiNoteObj        *biji_own_cloud_note_new_from_info           (BijiOwnCloudPro
 
     uuid_generate (unique);
     uuid_unparse_lower (unique, out);
-    
+
     info->url = g_strdup_printf ("%s/%s.txt",
                   biji_own_cloud_provider_get_readable_path (prov),
                   out);



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