[gnome-logs/wip/larsu/listmodel: 18/18] Add GlJournalModel



commit a27b21f8f144c4b308defbc65607853c608a4ade
Author: Lars Uebernickel <lars uebernic de>
Date:   Sat Feb 14 19:42:32 2015 +0100

    Add GlJournalModel
    
    A GListModel that contains all journal entries for a given set of
    matches.
    
    This let's us have a single listbox in GlEventViewList that is bound to
    this model.

 Makefile.am              |    2 +
 data/gl-eventviewlist.ui |    6 ++
 src/gl-eventviewlist.c   |  123 ++++++++---------------------
 src/gl-journal-model.c   |  192 ++++++++++++++++++++++++++++++++++++++++++++++
 src/gl-journal-model.h   |   15 ++++
 5 files changed, 249 insertions(+), 89 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index ec9deda..399ddf5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,6 +32,7 @@ gnome_logs_SOURCES = \
        src/gl-eventviewlist.c \
        src/gl-eventviewrow.c \
        src/gl-journal.c \
+       src/gl-journal-model.c \
        src/gl-main.c \
        src/gl-util.c \
        src/gl-window.c
@@ -60,6 +61,7 @@ noinst_gnome_logs_headers = \
        src/gl-eventviewlist.h \
        src/gl-eventviewrow.h \
        src/gl-journal.h \
+       src/gl-journal-model.h \
        src/gl-util.h \
        src/gl-window.h
 
diff --git a/data/gl-eventviewlist.ui b/data/gl-eventviewlist.ui
index 78a4699..61dfa98 100644
--- a/data/gl-eventviewlist.ui
+++ b/data/gl-eventviewlist.ui
@@ -37,6 +37,12 @@
                     <object class="GtkScrolledWindow" id="event_scrolled">
                         <property name="visible">True</property>
                         <property name="expand">True</property>
+                        <child>
+                            <object class="GtkListBox" id="entries_box">
+                                <property name="visible">True</property>
+                                <property name="selection-mode">none</property>
+                            </object>
+                        </child>
                     </object>
                 </child>
             </object>
diff --git a/src/gl-eventviewlist.c b/src/gl-eventviewlist.c
index 6602223..5c95cff 100644
--- a/src/gl-eventviewlist.c
+++ b/src/gl-eventviewlist.c
@@ -28,15 +28,15 @@
 #include "gl-eventview.h"
 #include "gl-eventviewdetail.h"
 #include "gl-eventviewrow.h"
-#include "gl-journal.h"
+#include "gl-journal-model.h"
 #include "gl-util.h"
 
 typedef struct
 {
-    GlJournal *journal;
+    GlJournalModel *journal_model;
     GlJournalEntry *entry;
     GlUtilClockFormat clock_format;
-    GtkListBox *active_listbox;
+    GtkListBox *entries_box;
     GtkWidget *categories;
     GtkWidget *event_search;
     GtkWidget *event_scrolled;
@@ -44,12 +44,10 @@ typedef struct
     gchar *search_text;
 
     GlEventViewRowStyle current_row_style;
-    guint insert_idle_id;
 } GlEventViewListPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (GlEventViewList, gl_event_view_list, GTK_TYPE_BOX)
 
-static const gssize N_RESULTS_IDLE = 5;
 static const gchar DESKTOP_SCHEMA[] = "org.gnome.desktop.interface";
 static const gchar SETTINGS_SCHEMA[] = "org.gnome.Logs";
 static const gchar CLOCK_FORMAT[] = "clock-format";
@@ -301,56 +299,14 @@ gl_event_view_create_empty (G_GNUC_UNUSED GlEventViewList *view)
 }
 
 static GtkWidget *
-gl_event_view_list_box_new (GlEventViewList *view)
+gl_event_list_view_create_row_widget (gpointer item,
+                                      gpointer user_data)
 {
-    GtkWidget *listbox;
+    GlEventViewList *view = user_data;
 
-    listbox = gtk_list_box_new ();
-
-    gtk_list_box_set_filter_func (GTK_LIST_BOX (listbox),
-                                  (GtkListBoxFilterFunc)listbox_search_filter_func,
-                                  view, NULL);
-    gtk_list_box_set_placeholder (GTK_LIST_BOX (listbox),
-                                  gl_event_view_create_empty (view));
-    gtk_list_box_set_selection_mode (GTK_LIST_BOX (listbox),
-                                     GTK_SELECTION_NONE);
-    g_signal_connect (listbox, "row-activated",
-                      G_CALLBACK (on_listbox_row_activated), GTK_BOX (view));
-
-    return listbox;
-}
-
-static gboolean
-insert_idle (gpointer data)
-{
-    GlEventViewList *view = data;
     GlEventViewListPrivate *priv = gl_event_view_list_get_instance_private (view);
-    gint i;
-
-    for (i = 0; i < N_RESULTS_IDLE; i++)
-    {
-        GlJournalEntry *entry;
-        GtkWidget *row;
-
-        entry = gl_journal_previous (priv->journal);
-        if (entry)
-        {
-            row = gl_event_view_row_new (entry,
-                                         priv->current_row_style,
-                                         priv->clock_format);
-            gtk_container_add (GTK_CONTAINER (priv->active_listbox), row);
-            gtk_widget_show_all (row);
-
-            g_object_unref (entry);
-        }
-        else
-        {
-            priv->insert_idle_id = 0;
-            return G_SOURCE_REMOVE;
-        }
-    }
 
-    return G_SOURCE_CONTINUE;
+    return gl_event_view_row_new (item, priv->current_row_style, priv->clock_format);
 }
 
 static gchar *
@@ -385,23 +341,6 @@ on_notify_category (GlCategoryList *list,
     priv = gl_event_view_list_get_instance_private (view);
     filter = gl_category_list_get_category (list);
 
-    if (priv->insert_idle_id)
-      {
-        g_source_remove (priv->insert_idle_id);
-        priv->insert_idle_id = 0;
-      }
-
-    if (priv->active_listbox)
-      {
-        GtkWidget *child;
-
-        child = gtk_bin_get_child (GTK_BIN (priv->event_scrolled));
-        gtk_widget_destroy (child);
-      }
-
-    priv->active_listbox = GTK_LIST_BOX (gl_event_view_list_box_new (view));
-    gtk_container_add (GTK_CONTAINER (priv->event_scrolled), GTK_WIDGET (priv->active_listbox));
-
     switch (filter)
     {
         case GL_CATEGORY_LIST_FILTER_IMPORTANT:
@@ -410,7 +349,7 @@ on_notify_category (GlCategoryList *list,
               const gchar * query[] = { "PRIORITY=0", "PRIORITY=1", "PRIORITY=2", "PRIORITY=3", NULL };
 
               priv->current_row_style = GL_EVENT_VIEW_ROW_STYLE_CMDLINE;
-              gl_journal_set_matches (priv->journal, query);
+              gl_journal_model_set_matches (priv->journal_model, query);
             }
             break;
 
@@ -419,7 +358,7 @@ on_notify_category (GlCategoryList *list,
                 const gchar *query[] = { NULL };
 
                 priv->current_row_style = GL_EVENT_VIEW_ROW_STYLE_CMDLINE;
-                gl_journal_set_matches (priv->journal, query);
+                gl_journal_model_set_matches (priv->journal_model, query);
             }
             break;
 
@@ -437,7 +376,7 @@ on_notify_category (GlCategoryList *list,
                 priv->current_row_style = GL_EVENT_VIEW_ROW_STYLE_CMDLINE;
                 uid_str = create_uid_match_string ();
                 query[3] = uid_str;
-                gl_journal_set_matches (priv->journal, query);
+                gl_journal_model_set_matches (priv->journal_model, query);
 
                 g_free (uid_str);
             }
@@ -448,7 +387,7 @@ on_notify_category (GlCategoryList *list,
                 const gchar *query[] = { "_TRANSPORT=kernel", NULL };
 
                 priv->current_row_style = GL_EVENT_VIEW_ROW_STYLE_SIMPLE;
-                gl_journal_set_matches (priv->journal, query);
+                gl_journal_model_set_matches (priv->journal_model, query);
             }
             break;
 
@@ -457,7 +396,7 @@ on_notify_category (GlCategoryList *list,
                 const gchar *query[] = { "_TRANSPORT=kernel", "_KERNEL_DEVICE", NULL };
 
                 priv->current_row_style = GL_EVENT_VIEW_ROW_STYLE_SIMPLE;
-                gl_journal_set_matches (priv->journal, query);
+                gl_journal_model_set_matches (priv->journal_model, query);
             }
             break;
 
@@ -466,7 +405,7 @@ on_notify_category (GlCategoryList *list,
                 const gchar *query[] = { "_AUDIT_SESSION", NULL };
 
                 priv->current_row_style = GL_EVENT_VIEW_ROW_STYLE_CMDLINE;
-                gl_journal_set_matches (priv->journal, query);
+                gl_journal_model_set_matches (priv->journal_model, query);
             }
             break;
 
@@ -474,10 +413,6 @@ on_notify_category (GlCategoryList *list,
             g_assert_not_reached ();
     }
 
-    priv->insert_idle_id = g_idle_add (insert_idle, view);
-
-    gtk_widget_show_all (GTK_WIDGET (priv->active_listbox));
-
     settings = g_settings_new (SETTINGS_SCHEMA);
     sort_order = g_settings_get_enum (settings, SORT_ORDER);
     g_object_unref (settings);
@@ -553,12 +488,12 @@ gl_event_view_list_set_sort_order (GlEventViewList *view,
     switch (sort_order)
     {
         case GL_SORT_ORDER_ASCENDING_TIME:
-            gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->active_listbox),
+            gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->entries_box),
                                         (GtkListBoxSortFunc) gl_event_view_sort_by_ascending_time,
                                         NULL, NULL);
             break;
         case GL_SORT_ORDER_DESCENDING_TIME:
-            gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->active_listbox),
+            gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->entries_box),
                                         (GtkListBoxSortFunc) gl_event_view_sort_by_descending_time,
                                         NULL, NULL);
             break;
@@ -615,11 +550,7 @@ gl_event_view_list_finalize (GObject *object)
     GlEventViewList *view = GL_EVENT_VIEW_LIST (object);
     GlEventViewListPrivate *priv = gl_event_view_list_get_instance_private (view);
 
-    if (priv->insert_idle_id)
-    {
-        g_source_remove (priv->insert_idle_id);
-    }
-
+    g_clear_object (&priv->journal_model);
     g_clear_pointer (&priv->search_text, g_free);
 }
 
@@ -634,6 +565,8 @@ gl_event_view_list_class_init (GlEventViewListClass *klass)
     gtk_widget_class_set_template_from_resource (widget_class,
                                                  "/org/gnome/Logs/gl-eventviewlist.ui");
     gtk_widget_class_bind_template_child_private (widget_class, GlEventViewList,
+                                                  entries_box);
+    gtk_widget_class_bind_template_child_private (widget_class, GlEventViewList,
                                                   categories);
     gtk_widget_class_bind_template_child_private (widget_class, GlEventViewList,
                                                   event_search);
@@ -659,11 +592,23 @@ gl_event_view_list_init (GlEventViewList *view)
 
     priv = gl_event_view_list_get_instance_private (view);
     priv->search_text = NULL;
-    priv->active_listbox = NULL;
-    priv->insert_idle_id = 0;
-    priv->journal = gl_journal_new ();
     categories = GL_CATEGORY_LIST (priv->categories);
 
+    priv->journal_model = gl_journal_model_new ();
+
+    gtk_list_box_bind_model (GTK_LIST_BOX (priv->entries_box),
+                             G_LIST_MODEL (priv->journal_model),
+                             gl_event_list_view_create_row_widget,
+                             view, NULL);
+
+    gtk_list_box_set_filter_func (GTK_LIST_BOX (priv->entries_box),
+                                  (GtkListBoxFilterFunc) listbox_search_filter_func,
+                                  view, NULL);
+    gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->entries_box),
+                                  gl_event_view_create_empty (view));
+    g_signal_connect (priv->entries_box, "row-activated",
+                      G_CALLBACK (on_listbox_row_activated), GTK_BOX (view));
+
     /* TODO: Monitor and propagate any GSettings changes. */
     settings = g_settings_new (DESKTOP_SCHEMA);
     priv->clock_format = g_settings_get_enum (settings, CLOCK_FORMAT);
@@ -686,7 +631,7 @@ gl_event_view_list_search (GlEventViewList *view,
     g_free (priv->search_text);
     priv->search_text = g_strdup (needle);
 
-    gtk_list_box_invalidate_filter (priv->active_listbox);
+    gtk_list_box_invalidate_filter (priv->entries_box);
 }
 
 GtkWidget *
diff --git a/src/gl-journal-model.c b/src/gl-journal-model.c
new file mode 100644
index 0000000..681fbe0
--- /dev/null
+++ b/src/gl-journal-model.c
@@ -0,0 +1,192 @@
+
+#include "gl-journal-model.h"
+#include "gl-journal.h"
+
+struct _GlJournalModel
+{
+  GObject parent_instance;
+
+  GlJournal *journal;
+  GPtrArray *entries;
+
+  guint idle_source;
+};
+
+static void gl_journal_model_interface_init (GListModelInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GlJournalModel, gl_journal_model, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gl_journal_model_interface_init))
+
+enum
+{
+  PROP_0,
+  PROP_MATCHES,
+  N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES];
+
+static gboolean
+gl_journal_model_fetch_entries (gpointer user_data)
+{
+  GlJournalModel *model = user_data;
+  guint last;
+  gint i;
+
+  last = model->entries->len;
+  for (i = 0; i < 5; i++)
+    {
+      GlJournalEntry *entry;
+
+      entry = gl_journal_previous (model->journal);
+      if (entry)
+        {
+          g_ptr_array_add (model->entries, entry);
+        }
+      else
+        {
+          model->idle_source = 0;
+          return G_SOURCE_REMOVE;
+        }
+    }
+
+  g_list_model_items_changed (G_LIST_MODEL (model), last, 0, i);
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+gl_journal_model_init (GlJournalModel *model)
+{
+  model->journal = gl_journal_new ();
+  model->entries = g_ptr_array_new_with_free_func (g_object_unref);
+  model->idle_source = g_idle_add_full (G_PRIORITY_LOW, gl_journal_model_fetch_entries, model, NULL);
+}
+
+static void
+gl_journal_model_set_property (GObject      *object,
+                               guint         property_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  GlJournalModel *model = GL_JOURNAL_MODEL (object);
+
+  switch (property_id)
+    {
+    case PROP_MATCHES:
+      gl_journal_model_set_matches (model, g_value_get_boxed (value));
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gl_journal_model_stop_idle (GlJournalModel *model)
+{
+  if (model->idle_source)
+    {
+      g_source_remove (model->idle_source);
+      model->idle_source = 0;
+    }
+}
+
+static void
+gl_journal_model_dispose (GObject *object)
+{
+  GlJournalModel *model = GL_JOURNAL_MODEL (object);
+
+  gl_journal_model_stop_idle (model);
+
+  if (model->entries)
+    {
+      g_ptr_array_free (model->entries, TRUE);
+      model->entries = NULL;
+    }
+
+  g_clear_object (&model->journal);
+
+  G_OBJECT_CLASS (gl_journal_model_parent_class)->dispose (object);
+}
+
+static GType
+gl_journal_model_get_item_type (GListModel *list)
+{
+  return GL_TYPE_JOURNAL_ENTRY;
+}
+
+static guint
+gl_journal_model_get_n_items (GListModel *list)
+{
+  GlJournalModel *model = GL_JOURNAL_MODEL (list);
+
+  return model->entries->len;
+}
+
+static gpointer
+gl_journal_model_get_item (GListModel *list,
+                           guint       position)
+{
+  GlJournalModel *model = GL_JOURNAL_MODEL (list);
+
+  if (position < model->entries->len)
+    return g_object_ref (g_ptr_array_index (model->entries, position));
+
+  return NULL;
+}
+
+static void
+gl_journal_model_class_init (GlJournalModelClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GParamFlags default_flags = G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY;
+
+  object_class->dispose = gl_journal_model_dispose;
+  object_class->set_property = gl_journal_model_set_property;
+
+  properties[PROP_MATCHES] = g_param_spec_boxed ("matches", "", "", G_TYPE_STRV,
+                                                 G_PARAM_WRITABLE | default_flags);
+
+  g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+}
+
+static void
+gl_journal_model_interface_init (GListModelInterface *iface)
+{
+  iface->get_item_type = gl_journal_model_get_item_type;
+  iface->get_n_items = gl_journal_model_get_n_items;
+  iface->get_item = gl_journal_model_get_item;
+}
+
+GlJournalModel *
+gl_journal_model_new (void)
+{
+  return g_object_new (GL_TYPE_JOURNAL_MODEL, NULL);
+}
+
+/**
+ * gl_journal_model_set_matches:
+ * @model: a #GlJournalModel
+ * @matches: new matches
+ *
+ * Changes @model's filter matches to @matches. This resets all items in
+ * the model, as they have to be requeried from the journal.
+ */
+void
+gl_journal_model_set_matches (GlJournalModel      *model,
+                              const gchar * const *matches)
+{
+  g_return_if_fail (GL_IS_JOURNAL_MODEL (model));
+  g_return_if_fail (matches != NULL);
+
+  gl_journal_model_stop_idle (model);
+  if (model->entries->len > 0)
+    {
+      g_list_model_items_changed (G_LIST_MODEL (model), 0, model->entries->len, 0);
+      g_ptr_array_remove_range (model->entries, 0, model->entries->len);
+    }
+
+  gl_journal_set_matches (model->journal, matches);
+
+  model->idle_source = g_idle_add_full (G_PRIORITY_LOW, gl_journal_model_fetch_entries, model, NULL);
+}
diff --git a/src/gl-journal-model.h b/src/gl-journal-model.h
new file mode 100644
index 0000000..18b9bb8
--- /dev/null
+++ b/src/gl-journal-model.h
@@ -0,0 +1,15 @@
+
+#ifndef GL_JOURNAL_MODEL_H
+#define GL_JOURNAL_MODEL_H
+
+#include <gio/gio.h>
+
+#define GL_TYPE_JOURNAL_MODEL gl_journal_model_get_type()
+G_DECLARE_FINAL_TYPE (GlJournalModel, gl_journal_model, GL, JOURNAL_MODEL, GObject)
+
+GlJournalModel *        gl_journal_model_new                            (void);
+
+void                    gl_journal_model_set_matches                    (GlJournalModel      *model,
+                                                                         const gchar * const *matches);
+
+#endif


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