[notmuch] [PATCH 2/2] Added backwards iterator to threads

Subject: [notmuch] [PATCH 2/2] Added backwards iterator to threads

Date: Sun, 21 Mar 2010 22:32:33 +0100

To: notmuch@notmuchmail.org

Cc:

From: Ruben Pollan


Added the functions notmuch_threads_move_to_prevoius,
notmuch_threads_move_to_last and  notmuch_threads_move_to_first to
notmuch library. With them is possible to iterate backwards on threads.

* notmuch_threads_move_to_prevoius do the opposite than
  notmuch_threads_move_to_next, getting the threads iterator one
  position backwards.

* notmuch_threads_move_to_last move the iterator to the first last
thread.

* notmuch_threads_move_to_first move the iterator to the first valid
  thread.

For it has been implemented notmuch_thread_list_t structur that stores
the thread_ids so the backwards iteration gets the thread_id in the same
order that was show on forward iteration.
---
 lib/notmuch.h |   28 ++++++++
 lib/query.cc  |  209 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 222 insertions(+), 15 deletions(-)

diff --git a/lib/notmuch.h b/lib/notmuch.h
index 0d9cb0f..62f4ad4 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -466,6 +466,15 @@ notmuch_threads_valid (notmuch_threads_t *threads);
 notmuch_thread_t *
 notmuch_threads_get (notmuch_threads_t *threads);
 
+/* Move the 'threads' iterator to the first thread.          
+ *
+ * After that the 'threads' iterator will be set to the first valid
+ * thread, so it can be use to iterate with
+ * notmuch_threads_move_to_next.
+ */
+void
+notmuch_threads_move_to_first (notmuch_threads_t *threads);
+
 /* Move the 'threads' iterator to the next thread.
  *
  * If 'threads' is already pointing at the last thread then the
@@ -479,6 +488,25 @@ notmuch_threads_get (notmuch_threads_t *threads);
 void
 notmuch_threads_move_to_next (notmuch_threads_t *threads);
 
+/* Move the 'threads' iterator to the last thread.
+ *
+ * After that the 'threads' iterator will be set to the last valid
+ * thread, so it can be use to iterate with
+ * notmuch_threads_move_to_previous.
+ */
+void
+notmuch_threads_move_to_last (notmuch_threads_t *threads);
+
+/* Move the 'threads' iterator to the previous thread.
+ *
+ * If 'threads' is already pointing at the first thread then the
+ * iterator will be moved to a point just beyond that first thread,
+ * (where notmuch_threads_valid will return FALSE and
+ * notmuch_threads_get will return NULL).
+ */
+void
+notmuch_threads_move_to_previous (notmuch_threads_t *threads);
+
 /* Destroy a notmuch_threads_t object.
  *
  * It's not strictly necessary to call this function. All memory from
diff --git a/lib/query.cc b/lib/query.cc
index 514a156..727f449 100644
--- a/lib/query.cc
+++ b/lib/query.cc
@@ -35,16 +35,29 @@ typedef struct _notmuch_mset_messages {
     notmuch_messages_t base;
     notmuch_database_t *notmuch;
     Xapian::MSetIterator iterator;
+    Xapian::MSetIterator iterator_begin;
     Xapian::MSetIterator iterator_end;
 } notmuch_mset_messages_t;
 
+typedef struct _notmuch_thread_node {
+    const char *thread_id;
+    struct _notmuch_thread_node *next;
+    struct _notmuch_thread_node *prev;
+} notmuch_thread_node_t;
+
+typedef struct _notmuch_thread_list {
+    notmuch_thread_node_t *head;
+    notmuch_thread_node_t *tail;
+    notmuch_thread_node_t *iterator;
+} notmuch_thread_list_t;
+
 struct _notmuch_threads {
     notmuch_query_t *query;
     GHashTable *threads;
     notmuch_messages_t *messages;
 
-    /* This thread ID is our iterator state. */
-    const char *thread_id;
+    /* thread list with the thread_id of the showed messages */
+    notmuch_thread_list_t *list;
 };
 
 notmuch_query_t *
@@ -86,6 +99,7 @@ static int
 _notmuch_messages_destructor (notmuch_mset_messages_t *messages)
 {
     messages->iterator.~MSetIterator ();
+    messages->iterator_begin.~MSetIterator ();
     messages->iterator_end.~MSetIterator ();
 
     return 0;
@@ -108,6 +122,7 @@ notmuch_query_search_messages (notmuch_query_t *query)
 	messages->base.iterator = NULL;
 	messages->notmuch = notmuch;
 	new (&messages->iterator) Xapian::MSetIterator ();
+	new (&messages->iterator_begin) Xapian::MSetIterator ();
 	new (&messages->iterator_end) Xapian::MSetIterator ();
 
 	talloc_set_destructor (messages, _notmuch_messages_destructor);
@@ -157,6 +172,7 @@ notmuch_query_search_messages (notmuch_query_t *query)
 	mset = enquire.get_mset (0, notmuch->xapian_db->get_doccount ());
 
 	messages->iterator = mset.begin ();
+	messages->iterator_begin = mset.begin ();
 	messages->iterator_end = mset.end ();
 
     } catch (const Xapian::Error &error) {
@@ -208,6 +224,16 @@ _notmuch_mset_messages_get (notmuch_messages_t *messages)
 }
 
 void
+_notmuch_mset_messages_move_to_first (notmuch_messages_t *messages)
+{
+    notmuch_mset_messages_t *mset_messages;
+
+    mset_messages = (notmuch_mset_messages_t *) messages;
+
+    mset_messages->iterator = mset_messages->iterator_begin;
+}
+
+void
 _notmuch_mset_messages_move_to_next (notmuch_messages_t *messages)
 {
     notmuch_mset_messages_t *mset_messages;
@@ -217,6 +243,113 @@ _notmuch_mset_messages_move_to_next (notmuch_messages_t *messages)
     mset_messages->iterator++;
 }
 
+void
+_notmuch_mset_messages_move_to_last (notmuch_messages_t *messages)
+{
+    notmuch_mset_messages_t *mset_messages;
+
+    mset_messages = (notmuch_mset_messages_t *) messages;
+
+    mset_messages->iterator = mset_messages->iterator_end;
+    mset_messages->iterator--;
+}
+
+void
+_notmuch_mset_messages_move_to_previous (notmuch_messages_t *messages)
+{
+    notmuch_mset_messages_t *mset_messages;
+
+    mset_messages = (notmuch_mset_messages_t *) messages;
+
+    if (mset_messages->iterator == mset_messages->iterator_begin)
+    {
+        /*
+         * Xapian iterators can not be beyond the first element, so we
+         * assign the iterator_end to mark the iterator as invalid in case
+         * of move_to_previous with the iterator at the beginning
+         */
+        mset_messages->iterator = mset_messages->iterator_end;
+    }
+    else if (_notmuch_mset_messages_valid (messages))
+    {
+        /*
+         * If is valid move the iterator. To emulate the same behavior 
+         * than notmuch_messages_t the iterator won't be updated if is 
+         * not valid
+         */
+        mset_messages->iterator--;
+    }
+}
+
+static notmuch_thread_list_t *
+_notmuch_thread_list_create (void *ctx)
+{
+    notmuch_thread_list_t *list;
+
+    list = talloc (ctx, notmuch_thread_list_t);
+    list->tail = NULL;
+    list->head = NULL;
+    list->iterator = NULL;
+
+    return list;
+}
+
+static void
+_notmuch_thread_list_append (notmuch_thread_list_t *list, const char *thread_id)
+{
+    list->iterator = talloc (list, notmuch_thread_node_t);
+    if (list->head == NULL)
+    {
+        list->head = list->iterator;
+        list->iterator->prev = NULL;
+    }
+    else
+    {
+        list->tail->next = list->iterator;
+        list->iterator->prev = list->tail;
+    }
+
+    list->iterator->thread_id = thread_id;
+    list->iterator->next = NULL;
+    list->tail = list->iterator;
+}
+
+static const char *
+_notmuch_thread_list_get_id (notmuch_thread_list_t *list)
+{
+    return list->iterator->thread_id;
+}
+
+static notmuch_bool_t
+_notmuch_thread_list_valid (notmuch_thread_list_t *list)
+{
+    return (list->iterator != NULL);
+}
+
+static void
+_notmuch_thread_list_move_to_first (notmuch_thread_list_t *list)
+{
+    list->iterator = list->head;
+}
+
+static void
+_notmuch_thread_list_move_to_next (notmuch_thread_list_t *list)
+{
+    list->iterator = list->iterator->next;
+}
+
+static void
+_notmuch_thread_list_move_to_last (notmuch_thread_list_t *list)
+{
+    list->iterator = list->tail;
+}
+
+static void
+_notmuch_thread_list_move_to_previous (notmuch_thread_list_t *list)
+{
+    list->iterator = list->iterator->prev;
+}
+
 /* Glib objects force use to use a talloc destructor as well, (but not
  * nearly as ugly as the for messages due to C++ objects). At
  * this point, I'd really like to have some talloc-friendly
@@ -244,16 +377,15 @@ notmuch_query_search_threads (notmuch_query_t *query)
 					      free, NULL);
 
     threads->messages = notmuch_query_search_messages (query);
+    threads->list = _notmuch_thread_list_create (threads);
     if (!notmuch_messages_valid (threads->messages))
-    {
-        threads->thread_id = NULL;
         return threads;
-    }
 
     message = notmuch_messages_get (threads->messages);
-    threads->thread_id = notmuch_message_get_thread_id (message);
+    _notmuch_thread_list_append (threads->list,
+                                 notmuch_message_get_thread_id (message));
     g_hash_table_insert (threads->threads,
-                         xstrdup (threads->thread_id),
+                         xstrdup (_notmuch_thread_list_get_id (threads->list)),
                          NULL);
 
     talloc_set_destructor (threads, _notmuch_threads_destructor);
@@ -270,7 +402,7 @@ notmuch_query_destroy (notmuch_query_t *query)
 notmuch_bool_t
 notmuch_threads_valid (notmuch_threads_t *threads)
 {
-    return (threads->thread_id != NULL);
+    return _notmuch_thread_list_valid (threads->list);
 }
 
 notmuch_thread_t *
@@ -281,35 +413,82 @@ notmuch_threads_get (notmuch_threads_t *threads)
 
     return _notmuch_thread_create (threads->query,
 				   threads->query->notmuch,
-				   threads->thread_id,
+				   _notmuch_thread_list_get_id (threads->list),
 				   threads->query->query_string);
 }
 
 void
+notmuch_threads_move_to_first (notmuch_threads_t *threads)
+{
+    _notmuch_thread_list_move_to_first (threads->list);
+}
+
+void
 notmuch_threads_move_to_next (notmuch_threads_t *threads)
 {
-    notmuch_message_t *message;
+    if (!_notmuch_thread_list_valid (threads->list))
+        return;
+
+    _notmuch_thread_list_move_to_next (threads->list);
+    if (_notmuch_thread_list_valid (threads->list))
+        return;
 
     while (notmuch_messages_valid (threads->messages))
     {
-	message = notmuch_messages_get (threads->messages);
+        notmuch_message_t *message;
+        const char *thread_id;
 
-	threads->thread_id = notmuch_message_get_thread_id (message);
+	message = notmuch_messages_get (threads->messages);
+	thread_id = notmuch_message_get_thread_id (message);
 
 	if (! g_hash_table_lookup_extended (threads->threads,
-					    threads->thread_id,
+					    thread_id,
 					    NULL, NULL))
 	{
 	    g_hash_table_insert (threads->threads,
-				 xstrdup (threads->thread_id), NULL);
+				 xstrdup (thread_id), NULL);
+            _notmuch_thread_list_append (threads->list, thread_id);
 	    notmuch_messages_move_to_next (threads->messages);
 	    return;
 	}
 
 	notmuch_messages_move_to_next (threads->messages);
     }
+}
+
+void
+notmuch_threads_move_to_last (notmuch_threads_t *threads)
+{
+    _notmuch_thread_list_move_to_last (threads->list);
+
+    while (notmuch_messages_valid (threads->messages))
+    {
+        notmuch_message_t *message;
+        const char *thread_id;
+
+	message = notmuch_messages_get (threads->messages);
+	thread_id = notmuch_message_get_thread_id (message);
+
+	if (! g_hash_table_lookup_extended (threads->threads,
+					    thread_id,
+					    NULL, NULL))
+	{
+	    g_hash_table_insert (threads->threads,
+				 xstrdup (thread_id), NULL);
+            _notmuch_thread_list_append (threads->list, thread_id);
+        }
+
+	notmuch_messages_move_to_next (threads->messages);
+    }
+}
+
+void
+notmuch_threads_move_to_previous (notmuch_threads_t *threads)
+{
+    if (!_notmuch_thread_list_valid (threads->list))
+        return;
 
-    threads->thread_id = NULL;
+    _notmuch_thread_list_move_to_previous (threads->list);
 }
 
 void
-- 
1.7.0


Thread: