[gnome-logs] Hide sidebar while viewing event details



commit afda11b8d616ee84d901e5904435c67131dc3cf5
Author: Jonathan Kang <jonathan121537 gmail com>
Date:   Thu Nov 6 14:11:21 2014 +0800

    Hide sidebar while viewing event details
    
    https://bugzilla.gnome.org/show_bug.cgi?id=726228

 Makefile.am                       |    4 +-
 data/gl-eventview.ui              |   10 +
 data/gl-eventviewlist.ui          |   45 +++++
 data/gl-window.ui                 |   42 +----
 data/org.gnome.Logs.gresource.xml |    2 +
 src/gl-application.c              |    2 +
 src/gl-categorylist.c             |  124 +++++++++---
 src/gl-categorylist.h             |   30 +++
 src/gl-eventview.c                |  253 ++++++++++++++++++++++++
 src/gl-eventview.h                |   65 ++++++
 src/gl-eventviewlist.c            |  394 ++++++++++++++++++-------------------
 src/gl-eventviewlist.h            |   54 +-----
 src/gl-window.c                   |  110 ++---------
 13 files changed, 723 insertions(+), 412 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 45d3c47..b1feb39 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,6 +27,7 @@ gnome_logs_SOURCES = \
        src/gl-application.c \
        src/gl-categorylist.c \
        src/gl-eventtoolbar.c \
+       src/gl-eventview.c \
        src/gl-eventviewdetail.c \
        src/gl-eventviewlist.c \
        src/gl-eventviewrow.c \
@@ -44,8 +45,9 @@ enum_data = \
        src/gl-enums.h
 
 gnome_logs_enum_headers = \
+       $(srcdir)/src/gl-categorylist.h \
        $(srcdir)/src/gl-eventtoolbar.h \
-       $(srcdir)/src/gl-eventviewlist.h \
+       $(srcdir)/src/gl-eventview.h \
        $(srcdir)/src/gl-eventviewrow.h \
        $(srcdir)/src/gl-util.h
 
diff --git a/data/gl-eventview.ui b/data/gl-eventview.ui
new file mode 100644
index 0000000..dde128a
--- /dev/null
+++ b/data/gl-eventview.ui
@@ -0,0 +1,10 @@
+<interface domain="gnome-logs">
+    <template class="GlEventView" parent="GtkStack">
+        <property name="visible">True</property>
+        <child>
+            <object class="GlEventViewList" id="events">
+                <property name="visible">True</property>
+            </object>
+        </child>
+    </template>
+</interface>
diff --git a/data/gl-eventviewlist.ui b/data/gl-eventviewlist.ui
new file mode 100644
index 0000000..1f7a9af
--- /dev/null
+++ b/data/gl-eventviewlist.ui
@@ -0,0 +1,45 @@
+<interface domain="gnome-logs">
+    <template class="GlEventViewList" parent="GtkBox">
+        <property name="orientation">horizontal</property>
+        <property name="visible">True</property>
+        <child>
+            <object class="GlCategoryList" id="categories">
+                <property name="visible">True</property>
+                <style>
+                    <class name="categories"/>
+                </style>
+            </object>
+        </child>
+        <child>
+            <object class="GtkSeparator" id="content_separator">
+                <property name="orientation">vertical</property>
+                <property name="visible">True</property>
+            </object>
+        </child>
+        <child>
+            <object class="GtkBox" id="event_view">
+                <property name="orientation">vertical</property>
+                <property name="visible">True</property>
+                <child>
+                    <object class="GtkSearchBar" id="event_search">
+                        <property name="visible">True</property>
+                        <signal name="notify::search-mode-enabled" 
handler="on_search_bar_notify_search_mode_enabled"/>
+                        <child>
+                            <object class="GtkSearchEntry" id="search_entry">
+                                <property name="visible">True</property>
+                                <property name="width-request">400</property>
+                                <signal name="search-changed" handler="on_search_entry_changed" />
+                            </object>
+                        </child>
+                    </object>
+                </child>
+                <child>
+                    <object class="GtkStack" id="event_stack">
+                        <property name="visible">True</property>
+                        <property name="expand">True</property>
+                    </object>
+                </child>
+            </object>
+        </child>
+    </template>
+</interface>
diff --git a/data/gl-window.ui b/data/gl-window.ui
index 4c67596..6f41df1 100644
--- a/data/gl-window.ui
+++ b/data/gl-window.ui
@@ -13,48 +13,8 @@
             </object>
         </child>
         <child>
-            <object class="GtkBox" id="content">
-                <property name="orientation">horizontal</property>
+            <object class="GlEventView" id="event">
                 <property name="visible">True</property>
-                <child>
-                    <object class="GlCategoryList" id="categories">
-                        <property name="visible">True</property>
-                        <style>
-                            <class name="categories"/>
-                        </style>
-                    </object>
-                </child>
-                <child>
-                    <object class="GtkSeparator" id="content_separator">
-                        <property name="orientation">vertical</property>
-                        <property name="visible">True</property>
-                    </object>
-                </child>
-                <child>
-                    <object class="GtkBox" id="event_view">
-                        <property name="orientation">vertical</property>
-                        <property name="visible">True</property>
-                        <child>
-                            <object class="GtkSearchBar" id="event_search">
-                                <property name="visible">True</property>
-                                <signal name="notify::search-mode-enabled" 
handler="on_gl_window_search_bar_notify_search_mode_enabled"/>
-                                <child>
-                                    <object class="GtkSearchEntry" id="search_entry">
-                                        <property name="visible">True</property>
-                                        <property name="width-request">400</property>
-                                        <signal name="search-changed" 
handler="on_gl_window_search_entry_changed" />
-                                    </object>
-                                </child>
-                            </object>
-                        </child>
-                        <child>
-                            <object class="GlEventViewList" id="events">
-                                <property name="expand">True</property>
-                                <property name="visible">True</property>
-                            </object>
-                        </child>
-                    </object>
-               </child>
             </object>
         </child>
     </template>
diff --git a/data/org.gnome.Logs.gresource.xml b/data/org.gnome.Logs.gresource.xml
index 2b0ec8a..46d38a3 100644
--- a/data/org.gnome.Logs.gresource.xml
+++ b/data/org.gnome.Logs.gresource.xml
@@ -2,6 +2,8 @@
 <gresources>
   <gresource prefix='/org/gnome/Logs'>
     <file preprocess='xml-stripblanks'>gl-categorylist.ui</file>
+    <file preprocess='xml-stripblanks'>gl-eventview.ui</file>
+    <file preprocess="xml-stripblanks">gl-eventviewlist.ui</file>
     <file preprocess='xml-stripblanks'>gl-eventtoolbar.ui</file>
     <file preprocess='xml-stripblanks'>gl-eventviewdetail.ui</file>
     <file>gl-style.css</file>
diff --git a/src/gl-application.c b/src/gl-application.c
index 49ccaaa..8f3dcd0 100644
--- a/src/gl-application.c
+++ b/src/gl-application.c
@@ -23,6 +23,7 @@
 
 #include "gl-categorylist.h"
 #include "gl-eventtoolbar.h"
+#include "gl-eventview.h"
 #include "gl-eventviewlist.h"
 #include "gl-util.h"
 #include "gl-window.h"
@@ -177,6 +178,7 @@ gl_application_startup (GApplication *application)
     gl_window_get_type ();
     gl_category_list_get_type ();
     gl_event_toolbar_get_type ();
+    gl_event_view_get_type ();
     gl_event_view_list_get_type ();
 }
 
diff --git a/src/gl-categorylist.c b/src/gl-categorylist.c
index 4d7e20f..d17bb08 100644
--- a/src/gl-categorylist.c
+++ b/src/gl-categorylist.c
@@ -23,6 +23,13 @@
 #include "gl-enums.h"
 #include "gl-eventviewlist.h"
 
+enum
+{
+    PROP_0,
+    PROP_CATEGORY,
+    N_PROPERTIES
+};
+
 typedef struct
 {
     GtkWidget *important;
@@ -35,10 +42,13 @@ typedef struct
     GtkWidget *hardware;
     GtkWidget *updates;
     GtkWidget *usage;
+    GlCategoryListFilter category;
 } GlCategoryListPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (GlCategoryList, gl_category_list, GTK_TYPE_LIST_BOX)
 
+static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+
 static gboolean
 gl_category_list_focus (GtkWidget *listbox, GtkDirectionType direction)
 {
@@ -75,86 +85,137 @@ on_gl_category_list_row_selected (GlCategoryList *listbox,
                                   gpointer user_data)
 {
     GlCategoryListPrivate *priv;
-    GtkWidget *toplevel;
-    GActionMap *appwindow;
-    GAction *category;
     GEnumClass *eclass;
     GEnumValue *evalue;
 
     priv = gl_category_list_get_instance_private (listbox);
-    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (listbox));
-
-    if (gtk_widget_is_toplevel (toplevel))
-    {
-        appwindow = G_ACTION_MAP (toplevel);
-        category = g_action_map_lookup_action (appwindow, "category");
-    }
-    else
-    {
-        /* TODO: Investigate whether this only happens during dispose. */
-        g_debug ("%s",
-                 "Category list row selected while not in a toplevel");
-        return;
-    }
-
-    eclass = g_type_class_ref (GL_TYPE_EVENT_VIEW_LIST_FILTER);
+    eclass = g_type_class_ref (GL_TYPE_CATEGORY_LIST_FILTER);
 
     if (row == GTK_LIST_BOX_ROW (priv->important))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_IMPORTANT);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_IMPORTANT);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->alerts))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_ALERTS);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_ALERTS);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->starred))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_STARRED);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_STARRED);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->all))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_ALL);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_ALL);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->applications))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_APPLICATIONS);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_APPLICATIONS);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->system))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_SYSTEM);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_SYSTEM);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->security))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_SECURITY);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_SECURITY);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->hardware))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_HARDWARE);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_HARDWARE);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->updates))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_UPDATES);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_UPDATES);
     }
     else if (row == GTK_LIST_BOX_ROW (priv->usage))
     {
-        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_LIST_FILTER_USAGE);
+        evalue = g_enum_get_value (eclass, GL_CATEGORY_LIST_FILTER_USAGE);
     }
     else
     {
-        g_assert_not_reached ();
+        /* This is only for the occasion when GlCategoryList is destroyed,
+         * in other words when there are no children for GlCategoryList */
+        return;
     }
 
-    g_action_activate (category, g_variant_new_string (evalue->value_nick));
+    priv->category = evalue->value;
+
+    g_object_notify_by_pspec (G_OBJECT (listbox),
+                              obj_properties[PROP_CATEGORY]);
 
     g_type_class_unref (eclass);
 }
 
+GlCategoryListFilter
+gl_category_list_get_category (GlCategoryList *list)
+{
+    GlCategoryListPrivate *priv;
+
+    priv = gl_category_list_get_instance_private (list);
+
+    return priv->category;
+}
+
+static void
+gl_category_list_get_property (GObject *object,
+                               guint prop_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+    GlCategoryList *list = GL_CATEGORY_LIST (object);
+    GlCategoryListPrivate *priv = gl_category_list_get_instance_private (list);
+
+    switch (prop_id)
+    {
+        case PROP_CATEGORY:
+            g_value_set_enum (value, priv->category);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gl_category_list_set_property (GObject *object,
+                               guint prop_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+    GlCategoryList *list = GL_CATEGORY_LIST (object);
+    GlCategoryListPrivate *priv = gl_category_list_get_instance_private (list);
+
+    switch (prop_id)
+    {
+        case PROP_CATEGORY:
+            priv->category = g_value_get_enum (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
 static void
 gl_category_list_class_init (GlCategoryListClass *klass)
 {
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+    gobject_class->get_property = gl_category_list_get_property;
+    gobject_class->set_property = gl_category_list_set_property;
     widget_class->focus = gl_category_list_focus;
+
+    obj_properties[PROP_CATEGORY] = g_param_spec_enum ("category", "Category",
+                                                       "Filter events by",
+                                                       GL_TYPE_CATEGORY_LIST_FILTER,
+                                                       GL_CATEGORY_LIST_FILTER_ALL,
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_STATIC_STRINGS);
+
+    g_object_class_install_properties (gobject_class, N_PROPERTIES,
+                                       obj_properties);
+
     gtk_widget_class_set_template_from_resource (widget_class,
                                                  "/org/gnome/Logs/gl-categorylist.ui");
     gtk_widget_class_bind_template_child_private (widget_class, GlCategoryList,
@@ -201,6 +262,7 @@ gl_category_list_init (GlCategoryList *list)
     GlCategoryListPrivate *priv;
 
     gtk_widget_init_template (GTK_WIDGET (list));
+
     priv = gl_category_list_get_instance_private (list);
     gtk_list_box_set_header_func (GTK_LIST_BOX (list),
                                   (GtkListBoxUpdateHeaderFunc)gl_category_list_header_func,
diff --git a/src/gl-categorylist.h b/src/gl-categorylist.h
index 3dd8cea..32f3b79 100644
--- a/src/gl-categorylist.h
+++ b/src/gl-categorylist.h
@@ -35,11 +35,41 @@ typedef struct
     GtkListBoxClass parent_class;
 } GlCategoryListClass;
 
+/*
+ * GlCategoryListFilter:
+ * @GL_CATEGORY_LIST_FILTER_IMPORTANT:
+ * @GL_CATEGORY_LIST_FILTER_ALERTS:
+ * @GL_CATEGORY_LIST_FILTER_STARRED:
+ * @GL_CATEGORY_LIST_FILTER_ALL:
+ * @GL_CATEGORY_LIST_FILTER_APPLICATIONS:
+ * @GL_CATEGORY_LIST_FILTER_SYSTEM:
+ * @GL_CATEGORY_LIST_FILTER_SECURITY:
+ * @GL_CATEGORY_LIST_FILTER_HARDWARE:
+ * @GL_CATEGORY_LIST_FILTER_UPDATES:
+ * @GL_CATEGORY_LIST_FILTER_USAGE:
+ *
+ * The category, selected in #GlCategoryList, to filter the events by.
+ */
+typedef enum
+{
+    GL_CATEGORY_LIST_FILTER_IMPORTANT,
+    GL_CATEGORY_LIST_FILTER_ALERTS,
+    GL_CATEGORY_LIST_FILTER_STARRED,
+    GL_CATEGORY_LIST_FILTER_ALL,
+    GL_CATEGORY_LIST_FILTER_APPLICATIONS,
+    GL_CATEGORY_LIST_FILTER_SYSTEM,
+    GL_CATEGORY_LIST_FILTER_SECURITY,
+    GL_CATEGORY_LIST_FILTER_HARDWARE,
+    GL_CATEGORY_LIST_FILTER_UPDATES,
+    GL_CATEGORY_LIST_FILTER_USAGE
+} GlCategoryListFilter;
+
 #define GL_TYPE_CATEGORY_LIST (gl_category_list_get_type ())
 #define GL_CATEGORY_LIST(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GL_TYPE_CATEGORY_LIST, 
GlCategoryList))
 
 GType gl_category_list_get_type (void);
 GtkWidget * gl_category_list_new (void);
+GlCategoryListFilter gl_category_list_get_category (GlCategoryList *list);
 
 G_END_DECLS
 
diff --git a/src/gl-eventview.c b/src/gl-eventview.c
new file mode 100644
index 0000000..1df5148
--- /dev/null
+++ b/src/gl-eventview.c
@@ -0,0 +1,253 @@
+/*
+ *  GNOME Logs - View and search logs
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *  Copyright (C) 2014  Jonathan Kang
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gl-eventview.h"
+
+#include <glib/gi18n.h>
+#include <glib-unix.h>
+#include <stdlib.h>
+
+#include "gl-categorylist.h"
+#include "gl-enums.h"
+#include "gl-eventtoolbar.h"
+#include "gl-eventviewdetail.h"
+#include "gl-eventviewlist.h"
+#include "gl-journal.h"
+#include "gl-util.h"
+
+enum
+{
+    PROP_0,
+    PROP_MODE,
+    N_PROPERTIES
+};
+
+typedef struct
+{
+    GtkWidget *events;
+    GlJournalResult *result;
+    GlUtilClockFormat clock_format;
+    GlEventViewMode mode;
+} GlEventViewPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GlEventView, gl_event_view, GTK_TYPE_STACK)
+
+static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+static const gchar DESKTOP_SCHEMA[] = "org.gnome.desktop.interface";
+static const gchar CLOCK_FORMAT[] = "clock-format";
+
+void
+gl_event_view_show_detail (GlEventView *view)
+{
+    GlEventViewList *events;
+    GlEventViewPrivate *priv;
+
+    g_return_if_fail (GL_EVENT_VIEW (view));
+
+    priv = gl_event_view_get_instance_private (view);
+    events = GL_EVENT_VIEW_LIST (priv->events);
+    priv->result = gl_event_view_list_get_detail_result (events);
+}
+
+gboolean
+gl_event_view_handle_search_event (GlEventView *view,
+                                   GAction *action,
+                                   GdkEvent *event)
+{
+    GlEventViewPrivate *priv;
+    GlEventViewList *events;
+
+    priv = gl_event_view_get_instance_private (view);
+    events = GL_EVENT_VIEW_LIST (priv->events);
+
+    if (gl_event_view_list_handle_search_event (events, action,
+                                                event) == GDK_EVENT_STOP)
+    {
+        return GDK_EVENT_STOP;
+    }
+
+    return GDK_EVENT_PROPAGATE;
+}
+
+void
+gl_event_view_set_search_mode (GlEventView *view,
+                               gboolean state)
+{
+    GlEventViewPrivate *priv;
+    GlEventViewList *events;
+
+    g_return_if_fail (GL_EVENT_VIEW (view));
+
+    priv = gl_event_view_get_instance_private (view);
+    events = GL_EVENT_VIEW_LIST (priv->events);
+
+    gl_event_view_list_set_search_mode (events, state);
+}
+
+static void
+on_notify_mode (GlEventView *view,
+                GParamSpec *pspec,
+                gpointer user_data)
+{
+    GlEventViewPrivate *priv;
+    GtkStack *stack;
+    GtkWidget *detail;
+
+    priv = gl_event_view_get_instance_private (view);
+    stack = GTK_STACK (view);
+
+    switch (priv->mode)
+    {
+        case GL_EVENT_VIEW_MODE_LIST:
+            {
+                GtkWidget *child;
+
+                child = gtk_stack_get_child_by_name (stack, "detail");
+
+                if (child)
+                {
+                    gtk_container_remove (GTK_CONTAINER (stack), child);
+                }
+
+                gtk_stack_set_visible_child (stack, priv->events);
+            }
+            break;
+        case GL_EVENT_VIEW_MODE_DETAIL:
+            {
+                gl_event_view_show_detail (view);
+                detail = gl_event_view_detail_new (priv->result,
+                                                   priv->clock_format);
+                gtk_widget_show_all (detail);
+                gtk_stack_add_named (stack, detail, "detail");
+                gtk_stack_set_visible_child_name (stack, "detail");
+            }
+            break;
+        default:
+            g_assert_not_reached ();
+            break;
+    }
+}
+
+static void
+gl_event_view_get_property (GObject *object,
+                            guint prop_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+    GlEventView *view = GL_EVENT_VIEW (object);
+    GlEventViewPrivate *priv = gl_event_view_get_instance_private (view);
+
+    switch (prop_id)
+    {
+        case PROP_MODE:
+            g_value_set_enum (value, priv->mode);
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gl_event_view_set_property (GObject *object,
+                            guint prop_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+    GlEventView *view = GL_EVENT_VIEW (object);
+    GlEventViewPrivate *priv = gl_event_view_get_instance_private (view);
+
+    switch (prop_id)
+    {
+        case PROP_MODE:
+            priv->mode = g_value_get_enum (value);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+            break;
+    }
+}
+
+static void
+gl_event_view_class_init (GlEventViewClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+    gobject_class->get_property = gl_event_view_get_property;
+    gobject_class->set_property = gl_event_view_set_property;
+
+    obj_properties[PROP_MODE] = g_param_spec_enum ("mode", "Mode",
+                                                   "Event display mode",
+                                                   GL_TYPE_EVENT_VIEW_MODE,
+                                                   GL_EVENT_VIEW_MODE_LIST,
+                                                   G_PARAM_READWRITE |
+                                                   G_PARAM_STATIC_STRINGS);
+
+    g_object_class_install_properties (gobject_class, N_PROPERTIES,
+                                       obj_properties);
+
+    gtk_widget_class_set_template_from_resource (widget_class,
+                                                 "/org/gnome/Logs/gl-eventview.ui");
+    gtk_widget_class_bind_template_child_private (widget_class, GlEventView,
+                                                  events);
+}
+
+static void
+gl_event_view_init (GlEventView *view)
+{
+    GlEventViewPrivate *priv;
+    GSettings *settings;
+
+    gtk_widget_init_template (GTK_WIDGET (view));
+
+    priv = gl_event_view_get_instance_private (view);
+
+    /* TODO: Monitor and propagate any GSettings changes. */
+    settings = g_settings_new (DESKTOP_SCHEMA);
+    priv->clock_format = g_settings_get_enum (settings, CLOCK_FORMAT);
+
+    g_object_unref (settings);
+
+    g_signal_connect (view, "notify::mode", G_CALLBACK (on_notify_mode),
+                      NULL);
+}
+
+void
+gl_event_view_set_mode (GlEventView *view,
+                        GlEventViewMode mode)
+{
+    GlEventViewPrivate *priv;
+
+    g_return_if_fail (GL_EVENT_VIEW (view));
+
+    priv = gl_event_view_get_instance_private (view);
+
+    if (priv->mode != mode)
+    {
+        priv->mode = mode;
+        g_object_notify_by_pspec (G_OBJECT (view),
+                                  obj_properties[PROP_MODE]);
+    }
+}
+
+GtkWidget *
+gl_event_view_new (void)
+{
+    return g_object_new (GL_TYPE_EVENT_VIEW, NULL);
+}
diff --git a/src/gl-eventview.h b/src/gl-eventview.h
new file mode 100644
index 0000000..67e6e1a
--- /dev/null
+++ b/src/gl-eventview.h
@@ -0,0 +1,65 @@
+/*
+ *  GNOME Logs - View and search logs
+ *  Copyright (C) 2014  Red Hat, Inc.
+ *  Copyright (C) 2014  Jonathan Kang
+ *
+ *  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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GL_EVENT_VIEW_H_
+#define GL_EVENT_VIEW_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+    /*< private >*/
+    GtkStack parent_instance;
+} GlEventView;
+
+typedef struct
+{
+    /*< private >*/
+    GtkStackClass parent_class;
+} GlEventViewClass;
+
+/*
+ * GlEventViewMode:
+ * @GL_EVENT_VIEW_MODE_LIST:
+ * @GL_EVENT_VIEW_MODE_DETAIL:
+ *
+ * The mode, mirroring the GlEventToolbar mode, used to show events.
+ */
+typedef enum
+{
+    GL_EVENT_VIEW_MODE_LIST,
+    GL_EVENT_VIEW_MODE_DETAIL
+} GlEventViewMode;
+
+#define GL_TYPE_EVENT_VIEW (gl_event_view_get_type ())
+#define GL_EVENT_VIEW(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GL_TYPE_EVENT_VIEW, GlEventView))
+
+GType gl_event_view_get_type (void);
+GtkWidget * gl_event_view_new (void);
+void gl_event_view_search (GlEventView *view, const gchar *needle);
+void gl_event_view_set_mode (GlEventView *view, GlEventViewMode mode);
+void gl_event_view_show_detail (GlEventView *view);
+gboolean gl_event_view_handle_search_event (GlEventView *view, GAction *action, GdkEvent *event);
+void gl_event_view_set_search_mode (GlEventView *view, gboolean state);
+
+G_END_DECLS
+
+#endif /* GL_EVENT_VIEW_H_ */
diff --git a/src/gl-eventviewlist.c b/src/gl-eventviewlist.c
index 1c51190..0588536 100644
--- a/src/gl-eventviewlist.c
+++ b/src/gl-eventviewlist.c
@@ -22,28 +22,25 @@
 #include <glib-unix.h>
 #include <stdlib.h>
 
+#include "gl-categorylist.h"
 #include "gl-enums.h"
 #include "gl-eventtoolbar.h"
+#include "gl-eventview.h"
 #include "gl-eventviewdetail.h"
 #include "gl-eventviewrow.h"
 #include "gl-journal.h"
 #include "gl-util.h"
 
-enum
-{
-    PROP_0,
-    PROP_FILTER,
-    PROP_MODE,
-    N_PROPERTIES
-};
-
 typedef struct
 {
     GlJournal *journal;
+    GlJournalResult *result;
     GlUtilClockFormat clock_format;
-    GlEventViewListFilter filter;
     GtkListBox *active_listbox;
-    GlEventViewMode mode;
+    GtkWidget *categories;
+    GtkWidget *event_search;
+    GtkWidget *event_stack;
+    GtkWidget *search_entry;
     gchar *search_text;
 
     GtkListBox *results_listbox;
@@ -52,9 +49,8 @@ typedef struct
     guint insert_idle_id;
 } GlEventViewListPrivate;
 
-G_DEFINE_TYPE_WITH_PRIVATE (GlEventViewList, gl_event_view_list, GTK_TYPE_STACK)
+G_DEFINE_TYPE_WITH_PRIVATE (GlEventViewList, gl_event_view_list, GTK_TYPE_BOX)
 
-static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
 static const gssize N_RESULTS = -1;
 static const gssize N_RESULTS_IDLE = 25;
 static const gchar DESKTOP_SCHEMA[] = "org.gnome.desktop.interface";
@@ -171,20 +167,87 @@ on_listbox_row_activated (GtkListBox *listbox,
                           GlEventViewList *view)
 {
     GlEventViewListPrivate *priv;
-    GlJournalResult *result;
-    GtkWidget *detail;
-    GtkStack *stack;
+    GtkWidget *toplevel;
 
     priv = gl_event_view_list_get_instance_private (view);
-    result = gl_event_view_row_get_result (GL_EVENT_VIEW_ROW (row));
+    priv->result = gl_event_view_row_get_result (GL_EVENT_VIEW_ROW (row));
+
+    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+
+    if (gtk_widget_is_toplevel (toplevel))
+    {
+        GAction *mode;
+        GEnumClass *eclass;
+        GEnumValue *evalue;
+
+        mode = g_action_map_lookup_action (G_ACTION_MAP (toplevel), "view-mode");
+        eclass = g_type_class_ref (GL_TYPE_EVENT_VIEW_MODE);
+        evalue = g_enum_get_value (eclass, GL_EVENT_VIEW_MODE_DETAIL);
+
+        g_action_activate (mode, g_variant_new_string (evalue->value_nick));
+
+        g_type_class_unref (eclass);
+    }
+    else
+    {
+        g_debug ("Widget not in toplevel window, not switching toolbar mode");
+    }
+}
 
-    detail = gl_event_view_detail_new (result, priv->clock_format);
+GlJournalResult *
+gl_event_view_list_get_detail_result (GlEventViewList *view)
+{
+    GlEventViewListPrivate *priv;
 
-    gtk_widget_show_all (detail);
+    priv = gl_event_view_list_get_instance_private (view);
 
-    stack = GTK_STACK (view);
-    gtk_stack_add_named (stack, detail, "detail");
-    gl_event_view_list_set_mode (view, GL_EVENT_VIEW_MODE_DETAIL);
+    return priv->result;
+}
+
+gboolean
+gl_event_view_list_handle_search_event (GlEventViewList *view,
+                                        GAction *action,
+                                        GdkEvent *event)
+{
+    GlEventViewListPrivate *priv;
+
+    priv = gl_event_view_list_get_instance_private (view);
+
+    if (g_action_get_enabled (action))
+    {
+        if (gtk_search_bar_handle_event (GTK_SEARCH_BAR (priv->event_search),
+                                         event) == GDK_EVENT_STOP)
+        {
+            g_action_change_state (action, g_variant_new_boolean (TRUE));
+
+            return GDK_EVENT_STOP;
+        }
+    }
+
+    return GDK_EVENT_PROPAGATE;
+}
+
+void
+gl_event_view_list_set_search_mode (GlEventViewList *view,
+                                    gboolean state)
+{
+    GlEventViewListPrivate *priv;
+
+    g_return_if_fail (GL_EVENT_VIEW_LIST (view));
+
+    priv = gl_event_view_list_get_instance_private (view);
+
+    gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->event_search), state);
+
+    if (state)
+    {
+        gtk_widget_grab_focus (priv->search_entry);
+        gtk_editable_set_position (GTK_EDITABLE (priv->search_entry), -1);
+    }
+    else
+    {
+        gtk_entry_set_text (GTK_ENTRY (priv->search_entry), "");
+    }
 }
 
 static GtkWidget *
@@ -238,7 +301,7 @@ gl_event_view_list_box_new (GlEventViewList *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_STACK (view));
+                      G_CALLBACK (on_listbox_row_activated), GTK_BOX (view));
 
     return listbox;
 }
@@ -583,7 +646,9 @@ gl_event_view_list_add_listbox_important (GlEventViewList *view)
                                                 NULL } };
     GtkWidget *listbox;
     GtkWidget *scrolled;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     listbox = gl_event_view_list_box_new (view);
 
     insert_journal_query_cmdline (view, &query,
@@ -592,27 +657,34 @@ gl_event_view_list_add_listbox_important (GlEventViewList *view)
     scrolled = gtk_scrolled_window_new (NULL, NULL);
     gtk_container_add (GTK_CONTAINER (scrolled), listbox);
     gtk_widget_show_all (scrolled);
-    gtk_stack_add_named (GTK_STACK (view), scrolled, "listbox-important");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), scrolled,
+                                    "listbox-important");
 }
 
 static void
 gl_event_view_list_add_listbox_alerts (GlEventViewList *view)
 {
     GtkWidget *label;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     label = gtk_label_new (_("Not implemented"));
     gtk_widget_show_all (label);
-    gtk_stack_add_named (GTK_STACK (view), label, "listbox-alerts");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), label,
+                         "listbox-alerts");
 }
 
 static void
 gl_event_view_list_add_listbox_starred (GlEventViewList *view)
 {
     GtkWidget *label;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     label = gtk_label_new (_("Not implemented"));
     gtk_widget_show_all (label);
-    gtk_stack_add_named (GTK_STACK (view), label, "listbox-starred");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), label,
+                         "listbox-starred");
 }
 
 static void
@@ -621,7 +693,9 @@ gl_event_view_list_add_listbox_all (GlEventViewList *view)
     const GlJournalQuery query = { N_RESULTS, NULL };
     GtkWidget *listbox;
     GtkWidget *scrolled;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     listbox = gl_event_view_list_box_new (view);
 
     insert_journal_query_cmdline (view, &query, GTK_LIST_BOX (listbox));
@@ -629,7 +703,8 @@ gl_event_view_list_add_listbox_all (GlEventViewList *view)
     scrolled = gtk_scrolled_window_new (NULL, NULL);
     gtk_container_add (GTK_CONTAINER (scrolled), listbox);
     gtk_widget_show_all (scrolled);
-    gtk_stack_add_named (GTK_STACK (view), scrolled, "listbox-all");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), scrolled,
+                         "listbox-all");
 }
 
 static void
@@ -639,7 +714,9 @@ gl_event_view_list_add_listbox_applications (GlEventViewList *view)
     uid_t uid;
     GtkWidget *listbox;
     GtkWidget *scrolled;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     listbox = gl_event_view_list_box_new (view);
     creds = g_credentials_new ();
     uid = g_credentials_get_unix_user (creds, NULL);
@@ -678,7 +755,8 @@ gl_event_view_list_add_listbox_applications (GlEventViewList *view)
     scrolled = gtk_scrolled_window_new (NULL, NULL);
     gtk_container_add (GTK_CONTAINER (scrolled), listbox);
     gtk_widget_show_all (scrolled);
-    gtk_stack_add_named (GTK_STACK (view), scrolled, "listbox-applications");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), scrolled,
+                                    "listbox-applications");
 
     g_object_unref (creds);
 }
@@ -690,7 +768,9 @@ gl_event_view_list_add_listbox_system (GlEventViewList *view)
                              (gchar *[2]){ "_TRANSPORT=kernel", NULL } };
     GtkWidget *listbox;
     GtkWidget *scrolled;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     listbox = gl_event_view_list_box_new (view);
 
     insert_journal_query_simple (view, &query, GTK_LIST_BOX (listbox));
@@ -698,7 +778,8 @@ gl_event_view_list_add_listbox_system (GlEventViewList *view)
     scrolled = gtk_scrolled_window_new (NULL, NULL);
     gtk_container_add (GTK_CONTAINER (scrolled), listbox);
     gtk_widget_show_all (scrolled);
-    gtk_stack_add_named (GTK_STACK (view), scrolled, "listbox-system");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), scrolled,
+                         "listbox-system");
 }
 
 static void
@@ -708,7 +789,9 @@ gl_event_view_list_add_listbox_hardware (GlEventViewList *view)
                              (gchar *[2]){ "_TRANSPORT=kernel", NULL } };
     GtkWidget *listbox;
     GtkWidget *scrolled;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     listbox = gl_event_view_list_box_new (view);
 
     insert_journal_query_devices (view, &query, GTK_LIST_BOX (listbox));
@@ -716,7 +799,8 @@ gl_event_view_list_add_listbox_hardware (GlEventViewList *view)
     scrolled = gtk_scrolled_window_new (NULL, NULL);
     gtk_container_add (GTK_CONTAINER (scrolled), listbox);
     gtk_widget_show_all (scrolled);
-    gtk_stack_add_named (GTK_STACK (view), scrolled, "listbox-hardware");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), scrolled,
+                         "listbox-hardware");
 }
 
 static void
@@ -725,7 +809,9 @@ gl_event_view_list_add_listbox_security (GlEventViewList *view)
     const GlJournalQuery query = { N_RESULTS, NULL };
     GtkWidget *listbox;
     GtkWidget *scrolled;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     listbox = gl_event_view_list_box_new (view);
 
     insert_journal_query_security (view, &query, GTK_LIST_BOX (listbox));
@@ -733,41 +819,52 @@ gl_event_view_list_add_listbox_security (GlEventViewList *view)
     scrolled = gtk_scrolled_window_new (NULL, NULL);
     gtk_container_add (GTK_CONTAINER (scrolled), listbox);
     gtk_widget_show_all (scrolled);
-    gtk_stack_add_named (GTK_STACK (view), scrolled, "listbox-security");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), scrolled,
+                         "listbox-security");
 }
 
 static void
 gl_event_view_list_add_listbox_updates (GlEventViewList *view)
 {
     GtkWidget *label;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     label = gtk_label_new (_("Not implemented"));
     gtk_widget_show_all (label);
-    gtk_stack_add_named (GTK_STACK (view), label, "listbox-updates");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), label,
+                         "listbox-updates");
 }
 
 static void
 gl_event_view_list_add_listbox_usage (GlEventViewList *view)
 {
     GtkWidget *label;
+    GlEventViewListPrivate *priv;
 
+    priv = gl_event_view_list_get_instance_private (view);
     label = gtk_label_new (_("Not implemented"));
     gtk_widget_show_all (label);
-    gtk_stack_add_named (GTK_STACK (view), label, "listbox-usage");
+    gtk_stack_add_named (GTK_STACK (priv->event_stack), label,
+                         "listbox-usage");
 }
 
 static void
-on_notify_filter (GlEventViewList *view,
-                  G_GNUC_UNUSED GParamSpec *pspec,
-                  G_GNUC_UNUSED gpointer user_data)
+on_notify_category (GlCategoryList *list,
+                    GParamSpec *pspec,
+                    gpointer user_data)
 {
+    GlCategoryListFilter filter;
+    GlEventViewList *view;
     GlEventViewListPrivate *priv;
     GtkStack *stack;
     GtkWidget *scrolled;
     GtkWidget *viewport;
 
+    view = GL_EVENT_VIEW_LIST (user_data);
     priv = gl_event_view_list_get_instance_private (view);
-    stack = GTK_STACK (view);
+    stack = GTK_STACK (priv->event_stack);
+    filter = gl_category_list_get_category (list);
 
     if (priv->active_listbox)
     {
@@ -777,45 +874,45 @@ on_notify_filter (GlEventViewList *view,
         gtk_widget_destroy (child);
     }
 
-    switch (priv->filter)
+    switch (filter)
     {
-        case GL_EVENT_VIEW_LIST_FILTER_IMPORTANT:
+        case GL_CATEGORY_LIST_FILTER_IMPORTANT:
             gl_event_view_list_add_listbox_important (view);
             gtk_stack_set_visible_child_name (stack, "listbox-important");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_ALERTS:
+        case GL_CATEGORY_LIST_FILTER_ALERTS:
             gl_event_view_list_add_listbox_alerts (view);
             gtk_stack_set_visible_child_name (stack, "listbox-alerts");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_STARRED:
+        case GL_CATEGORY_LIST_FILTER_STARRED:
             gl_event_view_list_add_listbox_starred (view);
             gtk_stack_set_visible_child_name (stack, "listbox-starred");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_ALL:
+        case GL_CATEGORY_LIST_FILTER_ALL:
             gl_event_view_list_add_listbox_all (view);
             gtk_stack_set_visible_child_name (stack, "listbox-all");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_APPLICATIONS:
+        case GL_CATEGORY_LIST_FILTER_APPLICATIONS:
             gl_event_view_list_add_listbox_applications (view);
             gtk_stack_set_visible_child_name (stack, "listbox-applications");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_SYSTEM:
+        case GL_CATEGORY_LIST_FILTER_SYSTEM:
             gl_event_view_list_add_listbox_system (view);
             gtk_stack_set_visible_child_name (stack, "listbox-system");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_HARDWARE:
+        case GL_CATEGORY_LIST_FILTER_HARDWARE:
             gl_event_view_list_add_listbox_hardware (view);
             gtk_stack_set_visible_child_name (stack, "listbox-hardware");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_SECURITY:
+        case GL_CATEGORY_LIST_FILTER_SECURITY:
             gl_event_view_list_add_listbox_security (view);
             gtk_stack_set_visible_child_name (stack, "listbox-security");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_UPDATES:
+        case GL_CATEGORY_LIST_FILTER_UPDATES:
             gl_event_view_list_add_listbox_updates (view);
             gtk_stack_set_visible_child_name (stack, "listbox-updates");
             break;
-        case GL_EVENT_VIEW_LIST_FILTER_USAGE:
+        case GL_CATEGORY_LIST_FILTER_USAGE:
             gl_event_view_list_add_listbox_usage (view);
             gtk_stack_set_visible_child_name (stack, "listbox-usage");
             break;
@@ -826,70 +923,46 @@ on_notify_filter (GlEventViewList *view,
     scrolled = gtk_stack_get_visible_child (stack);
     viewport = gtk_bin_get_child (GTK_BIN (scrolled));
     priv->active_listbox = GTK_LIST_BOX (gtk_bin_get_child (GTK_BIN (viewport)));
-
-    gl_event_view_list_set_mode (view, GL_EVENT_VIEW_MODE_LIST);
 }
 
 static void
-on_notify_mode (GlEventViewList *view,
-                GParamSpec *pspec,
-                gpointer user_data)
+on_search_entry_changed (GtkSearchEntry *entry,
+                         gpointer user_data)
 {
     GlEventViewListPrivate *priv;
-    GtkStack *stack;
-    GtkWidget *toplevel;
 
-    priv = gl_event_view_list_get_instance_private (view);
-    stack = GTK_STACK (view);
+    priv = gl_event_view_list_get_instance_private (GL_EVENT_VIEW_LIST (user_data));
 
-    switch (priv->mode)
-    {
-        case GL_EVENT_VIEW_MODE_LIST:
-            {
-                GtkWidget *child;
-                GtkWidget *viewport;
-                GtkWidget *scrolled_window;
-
-                child = gtk_stack_get_child_by_name (stack, "detail");
-
-                if (child)
-                {
-                    gtk_container_remove (GTK_CONTAINER (stack), child);
-                }
+    gl_event_view_list_search (GL_EVENT_VIEW_LIST (user_data),
+                               gtk_entry_get_text (GTK_ENTRY (priv->search_entry)));
+}
 
-                viewport = gtk_widget_get_parent (GTK_WIDGET (priv->active_listbox));
-                scrolled_window = gtk_widget_get_parent (viewport);
-                gtk_stack_set_visible_child (stack, scrolled_window);
-            }
-            break;
-        case GL_EVENT_VIEW_MODE_DETAIL:
-            gtk_stack_set_visible_child_name (stack, "detail");
-            break;
-        default:
-            g_assert_not_reached ();
-            break;
-    }
+static void
+on_search_bar_notify_search_mode_enabled (GtkSearchBar *search_bar,
+                                          GParamSpec *pspec,
+                                          gpointer user_data)
+{
+    GAction *search;
+    GtkWidget *toplevel;
+    GActionMap *appwindow;
 
-    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (user_data));
 
     if (gtk_widget_is_toplevel (toplevel))
     {
-        GAction *mode;
-        GEnumClass *eclass;
-        GEnumValue *evalue;
-
-        mode = g_action_map_lookup_action (G_ACTION_MAP (toplevel), "view-mode");
-        eclass = g_type_class_ref (GL_TYPE_EVENT_VIEW_MODE);
-        evalue = g_enum_get_value (eclass, priv->mode);
-
-        g_action_activate (mode, g_variant_new_string (evalue->value_nick));
-
-        g_type_class_unref (eclass);
+        appwindow = G_ACTION_MAP (toplevel);
+        search = g_action_map_lookup_action (appwindow, "search");
     }
     else
     {
-        g_debug ("Widget not in toplevel window, not switching toolbar mode");
+        /* TODO: Investigate whether this only happens during dispose. */
+        g_debug ("%s",
+                 "Search bar activated while not in a toplevel");
+        return;
     }
+
+    g_action_change_state (search,
+                           g_variant_new_boolean (gtk_search_bar_get_search_mode (search_bar)));
 }
 
 static void
@@ -908,102 +981,53 @@ gl_event_view_list_finalize (GObject *object)
 }
 
 static void
-gl_event_view_list_get_property (GObject *object,
-                            guint prop_id,
-                            GValue *value,
-                            GParamSpec *pspec)
-{
-    GlEventViewList *view = GL_EVENT_VIEW_LIST (object);
-    GlEventViewListPrivate *priv = gl_event_view_list_get_instance_private (view);
-
-    switch (prop_id)
-    {
-        case PROP_FILTER:
-            g_value_set_enum (value, priv->filter);
-            break;
-        case PROP_MODE:
-            g_value_set_enum (value, priv->mode);
-            break;
-        default:
-            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-            break;
-    }
-}
-
-static void
-gl_event_view_list_set_property (GObject *object,
-                                 guint prop_id,
-                                 const GValue *value,
-                                 GParamSpec *pspec)
-{
-    GlEventViewList *view = GL_EVENT_VIEW_LIST (object);
-    GlEventViewListPrivate *priv = gl_event_view_list_get_instance_private (view);
-
-    switch (prop_id)
-    {
-        case PROP_FILTER:
-            priv->filter = g_value_get_enum (value);
-            break;
-        case PROP_MODE:
-            priv->mode = g_value_get_enum (value);
-            break;
-        default:
-            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-            break;
-    }
-}
-
-static void
 gl_event_view_list_class_init (GlEventViewListClass *klass)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
     gobject_class->finalize = gl_event_view_list_finalize;
-    gobject_class->get_property = gl_event_view_list_get_property;
-    gobject_class->set_property = gl_event_view_list_set_property;
-
-    obj_properties[PROP_FILTER] = g_param_spec_enum ("filter", "Filter",
-                                                     "Filter events by",
-                                                     GL_TYPE_EVENT_VIEW_LIST_FILTER,
-                                                     GL_EVENT_VIEW_LIST_FILTER_IMPORTANT,
-                                                     G_PARAM_READWRITE |
-                                                     G_PARAM_STATIC_STRINGS);
-
-    obj_properties[PROP_MODE] = g_param_spec_enum ("mode", "Mode",
-                                                   "Event display mode",
-                                                   GL_TYPE_EVENT_VIEW_MODE,
-                                                   GL_EVENT_VIEW_MODE_LIST,
-                                                   G_PARAM_READWRITE |
-                                                   G_PARAM_STATIC_STRINGS);
-
-    g_object_class_install_properties (gobject_class, N_PROPERTIES,
-                                       obj_properties);
+
+    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,
+                                                  categories);
+    gtk_widget_class_bind_template_child_private (widget_class, GlEventViewList,
+                                                  event_search);
+    gtk_widget_class_bind_template_child_private (widget_class, GlEventViewList,
+                                                  event_stack);
+    gtk_widget_class_bind_template_child_private (widget_class, GlEventViewList,
+                                                  search_entry);
+
+    gtk_widget_class_bind_template_callback (widget_class,
+                                             on_search_entry_changed);
+    gtk_widget_class_bind_template_callback (widget_class,
+                                             on_search_bar_notify_search_mode_enabled);
 }
 
 static void
 gl_event_view_list_init (GlEventViewList *view)
 {
+    GlCategoryList *categories;
     GlEventViewListPrivate *priv;
     GSettings *settings;
 
+    gtk_widget_init_template (GTK_WIDGET (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);
 
     /* TODO: Monitor and propagate any GSettings changes. */
     settings = g_settings_new (DESKTOP_SCHEMA);
     priv->clock_format = g_settings_get_enum (settings, CLOCK_FORMAT);
     g_object_unref (settings);
 
-    g_signal_connect (view, "notify::filter", G_CALLBACK (on_notify_filter),
-                      NULL);
-    g_signal_connect (view, "notify::mode", G_CALLBACK (on_notify_mode),
-                      NULL);
-
-    /* Force an update of the active filter. */
-    on_notify_filter (view, NULL, NULL);
+    g_signal_connect (categories, "notify::category", G_CALLBACK (on_notify_category),
+                      view);
 }
 
 void
@@ -1022,42 +1046,6 @@ gl_event_view_list_search (GlEventViewList *view,
     gtk_list_box_invalidate_filter (priv->active_listbox);
 }
 
-void
-gl_event_view_list_set_filter (GlEventViewList *view,
-                               GlEventViewListFilter filter)
-{
-    GlEventViewListPrivate *priv;
-
-    g_return_if_fail (GL_EVENT_VIEW_LIST (view));
-
-    priv = gl_event_view_list_get_instance_private (view);
-
-    if (priv->filter != filter)
-    {
-        priv->filter = filter;
-        g_object_notify_by_pspec (G_OBJECT (view),
-                                  obj_properties[PROP_FILTER]);
-    }
-}
-
-void
-gl_event_view_list_set_mode (GlEventViewList *view,
-                             GlEventViewMode mode)
-{
-    GlEventViewListPrivate *priv;
-
-    g_return_if_fail (GL_EVENT_VIEW_LIST (view));
-
-    priv = gl_event_view_list_get_instance_private (view);
-
-    if (priv->mode != mode)
-    {
-        priv->mode = mode;
-        g_object_notify_by_pspec (G_OBJECT (view),
-                                  obj_properties[PROP_MODE]);
-    }
-}
-
 GtkWidget *
 gl_event_view_list_new (void)
 {
diff --git a/src/gl-eventviewlist.h b/src/gl-eventviewlist.h
index d25cb57..22d50a0 100644
--- a/src/gl-eventviewlist.h
+++ b/src/gl-eventviewlist.h
@@ -20,71 +20,33 @@
 #define GL_EVENT_VIEW_LIST_H_
 
 #include <gtk/gtk.h>
+#include "gl-journal.h"
 
 G_BEGIN_DECLS
 
 typedef struct
 {
     /*< private >*/
-    GtkListBox parent_instance;
+    GtkBox parent_instance;
 } GlEventViewList;
 
 typedef struct
 {
     /*< private >*/
-    GtkListBoxClass parent_class;
+    GtkBoxClass parent_class;
 } GlEventViewListClass;
 
-/*
- * GlEventViewListFilter:
- * @GL_EVENT_VIEW_LIST_FILTER_IMPORTANT:
- * @GL_EVENT_VIEW_LIST_FILTER_ALERTS:
- * @GL_EVENT_VIEW_LIST_FILTER_STARRED:
- * @GL_EVENT_VIEW_LIST_FILTER_ALL:
- * @GL_EVENT_VIEW_LIST_FILTER_APPLICATIONS:
- * @GL_EVENT_VIEW_LIST_FILTER_SYSTEM:
- * @GL_EVENT_VIEW_LIST_FILTER_SECURITY:
- * @GL_EVENT_VIEW_LIST_FILTER_HARDWARE:
- * @GL_EVENT_VIEW_LIST_FILTER_UPDATES:
- * @GL_EVENT_VIEW_LIST_FILTER_USAGE:
- *
- * The category, selected in #GlCategoryList, to filter the events by.
- */
-typedef enum
-{
-    GL_EVENT_VIEW_LIST_FILTER_IMPORTANT,
-    GL_EVENT_VIEW_LIST_FILTER_ALERTS,
-    GL_EVENT_VIEW_LIST_FILTER_STARRED,
-    GL_EVENT_VIEW_LIST_FILTER_ALL,
-    GL_EVENT_VIEW_LIST_FILTER_APPLICATIONS,
-    GL_EVENT_VIEW_LIST_FILTER_SYSTEM,
-    GL_EVENT_VIEW_LIST_FILTER_SECURITY,
-    GL_EVENT_VIEW_LIST_FILTER_HARDWARE,
-    GL_EVENT_VIEW_LIST_FILTER_UPDATES,
-    GL_EVENT_VIEW_LIST_FILTER_USAGE
-} GlEventViewListFilter;
-
-/*
- * GlEventViewMode:
- * @GL_EVENT_VIEW_MODE_LIST:
- * @GL_EVENT_VIEW_MODE_DETAIL:
- *
- * The mode, mirroring the GlEventToolbar mode, used to show events.
- */
-typedef enum
-{
-    GL_EVENT_VIEW_MODE_LIST,
-    GL_EVENT_VIEW_MODE_DETAIL
-} GlEventViewMode;
-
 #define GL_TYPE_EVENT_VIEW_LIST (gl_event_view_list_get_type ())
 #define GL_EVENT_VIEW_LIST(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GL_TYPE_EVENT_VIEW_LIST, 
GlEventViewList))
 
 GType gl_event_view_list_get_type (void);
 GtkWidget * gl_event_view_list_new (void);
 void gl_event_view_list_search (GlEventViewList *view, const gchar *needle);
-void gl_event_view_list_set_filter (GlEventViewList *view, GlEventViewListFilter filter);
-void gl_event_view_list_set_mode (GlEventViewList *view, GlEventViewMode mode);
+GlJournalResult *gl_event_view_list_get_detail_result (GlEventViewList *view);
+gboolean gl_event_view_list_handle_search_event (GlEventViewList *view,
+                                                 GAction *action,
+                                                 GdkEvent *event);
+void gl_event_view_list_set_search_mode (GlEventViewList *view, gboolean state);
 
 G_END_DECLS
 
diff --git a/src/gl-window.c b/src/gl-window.c
index fbd93ae..fc6f6b6 100644
--- a/src/gl-window.c
+++ b/src/gl-window.c
@@ -22,6 +22,7 @@
 
 #include "gl-categorylist.h"
 #include "gl-eventtoolbar.h"
+#include "gl-eventview.h"
 #include "gl-eventviewlist.h"
 #include "gl-enums.h"
 #include "gl-util.h"
@@ -29,9 +30,7 @@
 typedef struct
 {
     GtkWidget *event_toolbar;
-    GtkWidget *event_search;
-    GtkWidget *search_entry;
-    GtkWidget *events;
+    GtkWidget *event;
 } GlWindowPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (GlWindow, gl_window, GTK_TYPE_APPLICATION_WINDOW)
@@ -59,34 +58,7 @@ on_action_toggle (GSimpleAction *action,
 }
 
 static void
-on_category (GSimpleAction *action,
-             GVariant *variant,
-             gpointer user_data)
-{
-    GlWindowPrivate *priv;
-    const gchar *category;
-    GlEventViewList *events;
-    GEnumClass *eclass;
-    GEnumValue *evalue;
-
-    priv = gl_window_get_instance_private (GL_WINDOW (user_data));
-    category = g_variant_get_string (variant, NULL);
-    events = GL_EVENT_VIEW_LIST (priv->events);
-    eclass = g_type_class_ref (GL_TYPE_EVENT_VIEW_LIST_FILTER);
-    evalue = g_enum_get_value_by_nick (eclass, category);
-
-    /* First switch the event view back to list mode if the category
-       tab is clicked. */
-    gl_event_view_list_set_mode (events, GL_EVENT_VIEW_MODE_LIST);
-    gl_event_view_list_set_filter (events, evalue->value);
-
-    g_simple_action_set_state (action, variant);
-
-    g_type_class_unref (eclass);
-}
-
-static void
-on_close (GSimpleAction *action, 
+on_close (GSimpleAction *action,
           GVariant *variant,
           gpointer user_data)
 {
@@ -118,11 +90,11 @@ on_toolbar_mode (GSimpleAction *action,
     {
         /* Switch the event view back to list mode if the back button is
          * clicked. */
-        GlEventViewList *view;
+        GlEventView *view;
 
-        view = GL_EVENT_VIEW_LIST (priv->events);
+        view = GL_EVENT_VIEW (priv->event);
 
-        gl_event_view_list_set_mode (view, GL_EVENT_VIEW_MODE_LIST);
+        gl_event_view_set_mode (view, GL_EVENT_VIEW_MODE_LIST);
 
         g_simple_action_set_enabled (G_SIMPLE_ACTION (search), TRUE);
     }
@@ -145,11 +117,13 @@ on_view_mode (GSimpleAction *action,
     GlWindowPrivate *priv;
     const gchar *mode;
     GlEventToolbar *toolbar;
+    GlEventView *event;
     GEnumClass *eclass;
     GEnumValue *evalue;
 
     priv = gl_window_get_instance_private (GL_WINDOW (user_data));
     mode = g_variant_get_string (variant, NULL);
+    event = GL_EVENT_VIEW (priv->event);
     toolbar = GL_EVENT_TOOLBAR (priv->event_toolbar);
     eclass = g_type_class_ref (GL_TYPE_EVENT_VIEW_MODE);
     evalue = g_enum_get_value_by_nick (eclass, mode);
@@ -161,6 +135,7 @@ on_view_mode (GSimpleAction *action,
             break;
         case GL_EVENT_VIEW_MODE_DETAIL:
             gl_event_toolbar_set_mode (toolbar, GL_EVENT_TOOLBAR_MODE_DETAIL);
+            gl_event_view_set_mode (event, GL_EVENT_VIEW_MODE_DETAIL);
             break;
     }
 
@@ -176,21 +151,13 @@ on_search (GSimpleAction *action,
 {
     gboolean state;
     GlWindowPrivate *priv;
+    GlEventView *view;
 
     state = g_variant_get_boolean (variant);
     priv = gl_window_get_instance_private (GL_WINDOW (user_data));
+    view = GL_EVENT_VIEW (priv->event);
 
-    gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->event_search), state);
-
-    if (state)
-    {
-        gtk_widget_grab_focus (priv->search_entry);
-        gtk_editable_set_position (GTK_EDITABLE (priv->search_entry), -1);
-    }
-    else
-    {
-        gtk_entry_set_text (GTK_ENTRY (priv->search_entry), "");
-    }
+    gl_event_view_set_search_mode (view, state);
 
     g_simple_action_set_state (action, variant);
 }
@@ -201,6 +168,7 @@ on_gl_window_key_press_event (GlWindow *window,
                               gpointer user_data)
 {
     GlWindowPrivate *priv;
+    GlEventView *view;
     GAction *action;
     GVariant *variant;
     const gchar *mode;
@@ -209,16 +177,11 @@ on_gl_window_key_press_event (GlWindow *window,
 
     priv = gl_window_get_instance_private (window);
     action = g_action_map_lookup_action (G_ACTION_MAP (window), "search");
+    view = GL_EVENT_VIEW (priv->event);
 
-    if (g_action_get_enabled (action))
+    if (gl_event_view_handle_search_event (view, action, event) == GDK_EVENT_STOP)
     {
-        if (gtk_search_bar_handle_event (GTK_SEARCH_BAR (priv->event_search),
-                                         event) == GDK_EVENT_STOP)
-        {
-            g_action_change_state (action, g_variant_new_boolean (TRUE));
-
-            return GDK_EVENT_STOP;
-        }
+        return GDK_EVENT_STOP;
     }
 
     action = g_action_map_lookup_action (G_ACTION_MAP (window), "view-mode");
@@ -238,10 +201,10 @@ on_gl_window_key_press_event (GlWindow *window,
                                                            (GdkEventKey*)event)
                 == GDK_EVENT_STOP)
             {
-                GlEventViewList *events;
+                GlEventView *view;
 
-                events = GL_EVENT_VIEW_LIST (priv->events);
-                gl_event_view_list_set_mode (events, GL_EVENT_VIEW_MODE_LIST);
+                view = GL_EVENT_VIEW (priv->event);
+                gl_event_view_set_mode (view, GL_EVENT_VIEW_MODE_LIST);
                 g_type_class_unref (eclass);
 
                 return GDK_EVENT_STOP;
@@ -254,32 +217,7 @@ on_gl_window_key_press_event (GlWindow *window,
     return GDK_EVENT_PROPAGATE;
 }
 
-static void
-on_gl_window_search_entry_changed (GtkSearchEntry *entry,
-                                   gpointer user_data)
-{
-    GlWindowPrivate *priv;
-
-    priv = gl_window_get_instance_private (GL_WINDOW (user_data));
-
-    gl_event_view_list_search (GL_EVENT_VIEW_LIST (priv->events),
-                               gtk_entry_get_text (GTK_ENTRY (priv->search_entry)));
-}
-
-static void
-on_gl_window_search_bar_notify_search_mode_enabled (GtkSearchBar *search_bar,
-                                                    GParamSpec *pspec,
-                                                    gpointer user_data)
-{
-    GAction *search;
-
-    search = g_action_map_lookup_action (G_ACTION_MAP (user_data), "search");
-    g_action_change_state (search,
-                           g_variant_new_boolean (gtk_search_bar_get_search_mode (search_bar)));
-}
-
 static GActionEntry actions[] = {
-    { "category", on_action_radio, "s", "'important'", on_category },
     { "view-mode", on_action_radio, "s", "'list'", on_view_mode },
     { "toolbar-mode", on_action_radio, "s", "'list'", on_toolbar_mode },
     { "search", on_action_toggle, NULL, "false", on_search },
@@ -296,18 +234,10 @@ gl_window_class_init (GlWindowClass *klass)
     gtk_widget_class_bind_template_child_private (widget_class, GlWindow,
                                                   event_toolbar);
     gtk_widget_class_bind_template_child_private (widget_class, GlWindow,
-                                                  event_search);
-    gtk_widget_class_bind_template_child_private (widget_class, GlWindow,
-                                                  search_entry);
-    gtk_widget_class_bind_template_child_private (widget_class, GlWindow,
-                                                  events);
+                                                  event);
 
     gtk_widget_class_bind_template_callback (widget_class,
                                              on_gl_window_key_press_event);
-    gtk_widget_class_bind_template_callback (widget_class,
-                                             on_gl_window_search_entry_changed);
-    gtk_widget_class_bind_template_callback (widget_class,
-                                             on_gl_window_search_bar_notify_search_mode_enabled);
 }
 
 static void


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