[gnome-logs] Use GtkModelButton to improve boot selection menu



commit 013a62cd56eb204d1a82dbdaef08c1528ee0b55d
Author: Jonathan Kang <jonathan121537 gmail com>
Date:   Thu Aug 20 17:16:27 2015 +0800

    Use GtkModelButton to improve boot selection menu
    
    https://bugzilla.gnome.org/show_bug.cgi?id=752963

 data/gl-eventtoolbar.ui |    9 ++---
 data/gl-style.css       |    5 +++
 src/gl-eventtoolbar.c   |   82 ++++++++++++++++++++++++++++++++++++----------
 src/gl-eventtoolbar.h   |    2 +
 src/gl-eventview.c      |   13 +++++++
 src/gl-eventview.h      |    2 +
 src/gl-eventviewlist.c  |   12 +++++++
 src/gl-eventviewlist.h  |    2 +
 src/gl-journal-model.c  |    7 ++++
 src/gl-journal-model.h  |    3 ++
 src/gl-journal.c        |   62 +++++++++++++++++++++++++++++++++--
 src/gl-journal.h        |    5 ++-
 src/gl-util.c           |   38 ++++++++++++++++++++++
 src/gl-util.h           |    2 +
 src/gl-window.c         |   29 ++++++++++++++++-
 15 files changed, 244 insertions(+), 29 deletions(-)
---
diff --git a/data/gl-eventtoolbar.ui b/data/gl-eventtoolbar.ui
index c9bc84a..c42af57 100644
--- a/data/gl-eventtoolbar.ui
+++ b/data/gl-eventtoolbar.ui
@@ -2,7 +2,6 @@
     <template class="GlEventToolbar" parent="GtkHeaderBar">
         <property name="expand">True</property>
         <property name="show-close-button">True</property>
-        <property name="title" translatable="yes">Logs</property>
         <property name="visible">True</property>
         <child>
             <object class="GtkButton" id="back_button">
@@ -21,19 +20,17 @@
                 </child>
             </object>
         </child>
-        <child>
+        <child type="title">
             <object class="GtkMenuButton" id="menu_button">
                 <property name="direction">none</property>
                 <property name="tooltip-text" translatable="yes">Choose the boot from which to view 
logs</property>
                 <property name="valign">center</property>
                 <property name="visible">True</property>
+                <property name="relief">GTK_RELIEF_NONE</property>
                 <style>
-                    <class name="image-button"/>
+                    <class name="title-menu-button"/>
                 </style>
             </object>
-            <packing>
-                <property name="pack-type">end</property>
-            </packing>
         </child>
         <child>
             <object class="GtkToggleButton" id="search_button">
diff --git a/data/gl-style.css b/data/gl-style.css
index b67aba1..491ee90 100644
--- a/data/gl-style.css
+++ b/data/gl-style.css
@@ -28,3 +28,8 @@
 .event-time {
     font-size: small;
 }
+
+.title-menu-button {
+    padding-top: 0px;
+    padding-bottom: 0px;
+}
diff --git a/src/gl-eventtoolbar.c b/src/gl-eventtoolbar.c
index e1132d5..2da145b 100644
--- a/src/gl-eventtoolbar.c
+++ b/src/gl-eventtoolbar.c
@@ -39,6 +39,7 @@ struct _GlEventToolbar
 
 typedef struct
 {
+    GtkWidget *current_boot;
     GtkWidget *back_button;
     GtkWidget *menu_button;
     GtkWidget *search_button;
@@ -52,30 +53,40 @@ static const gchar CLOCK_FORMAT[] = "clock-format";
 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
 
 void
+gl_event_toolbar_change_current_boot (GlEventToolbar *toolbar,
+                                      const gchar *current_boot)
+{
+    GlEventToolbarPrivate *priv;
+
+    priv = gl_event_toolbar_get_instance_private (toolbar);
+
+    /* set text to priv->current_boot */
+    gtk_label_set_text (GTK_LABEL (priv->current_boot), current_boot);
+}
+
+void
 gl_event_toolbar_add_boots (GlEventToolbar *toolbar,
                            GArray *boot_ids)
 {
-    gint i;
+    GtkWidget *grid;
+    GtkWidget *title_label;
+    GtkWidget *arrow;
     GMenu *boot_menu;
+    GMenu *section;
     GlEventToolbarPrivate *priv;
-    GSettings *settings;
-    GlUtilClockFormat clock_format;
+    GtkStyleContext *context;
+    gint i;
+    gchar *current_boot = NULL;
 
     priv = gl_event_toolbar_get_instance_private (toolbar);
 
-    /* TODO: Monitor and propagate any GSettings changes. */
-    settings = g_settings_new (DESKTOP_SCHEMA);
-    clock_format = g_settings_get_enum (settings, CLOCK_FORMAT);
-
-    g_object_unref (settings);
-
     boot_menu = g_menu_new ();
+    section = g_menu_new ();
 
     for (i = boot_ids->len - 1; i >= boot_ids->len - 5 && i >= 0; i--)
     {
         gchar *boot_match;
-        gchar *time;
-        GDateTime *now;
+        gchar *time_display;
         GlJournalBootID *boot_id;
         GMenuItem *item;
         GVariant *variant;
@@ -83,23 +94,52 @@ gl_event_toolbar_add_boots (GlEventToolbar *toolbar,
         boot_id = &g_array_index (boot_ids, GlJournalBootID, i);
         boot_match = boot_id->boot_match;
 
-        now = g_date_time_new_now_local ();
-        time = gl_util_timestamp_to_display (boot_id->realtime,
-                                             now, clock_format);
+        time_display = gl_util_boot_time_to_display (boot_id->realtime_first,
+                                                     boot_id->realtime_last);
+        if (i == boot_ids->len - 1)
+        {
+            current_boot = g_strdup (time_display);
+        }
 
-        item = g_menu_item_new (time, NULL);
+        item = g_menu_item_new (time_display, NULL);
         variant = g_variant_new_string (boot_match);
         g_menu_item_set_action_and_target_value (item, "win.view-boot",
                                                  variant);
-        g_menu_append_item (boot_menu, item);
+        g_menu_append_item (section, item);
 
-        g_date_time_unref (now);
-        g_free (time);
+        g_free (time_display);
         g_object_unref (item);
     }
 
+    /* Translators: Boot refers to a single run (or bootup) of the system */
+    g_menu_prepend_section (boot_menu, _("Boot"), G_MENU_MODEL (section));
+
     gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->menu_button),
                                     G_MENU_MODEL (boot_menu));
+
+    grid = gtk_grid_new ();
+    gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
+    gtk_container_add (GTK_CONTAINER (priv->menu_button), grid);
+
+    title_label = gtk_label_new ("Logs");
+    context = gtk_widget_get_style_context (GTK_WIDGET (title_label));
+    gtk_style_context_add_class (context, "title");
+    gtk_grid_attach (GTK_GRID (grid), title_label, 0, 0, 1, 1);
+
+    gtk_label_set_text (GTK_LABEL (priv->current_boot), current_boot);
+    context = gtk_widget_get_style_context (GTK_WIDGET (priv->current_boot));
+    gtk_style_context_add_class (context, "subtitle");
+    gtk_grid_attach (GTK_GRID (grid), priv->current_boot, 0, 1, 1, 1);
+
+    arrow = gtk_image_new_from_icon_name ("pan-down-symbolic",
+                                          GTK_ICON_SIZE_BUTTON);
+    gtk_grid_attach (GTK_GRID (grid), arrow, 1, 0, 1, 2);
+    gtk_widget_show_all (grid);
+
+    gtk_header_bar_set_custom_title (GTK_HEADER_BAR (toolbar),
+                                     priv->menu_button);
+
+    g_free (current_boot);
 }
 
 static void
@@ -235,8 +275,14 @@ gl_event_toolbar_class_init (GlEventToolbarClass *klass)
 static void
 gl_event_toolbar_init (GlEventToolbar *toolbar)
 {
+    GlEventToolbarPrivate *priv;
+
+    priv = gl_event_toolbar_get_instance_private (toolbar);
+
     gtk_widget_init_template (GTK_WIDGET (toolbar));
 
+    priv->current_boot = gtk_label_new (NULL);
+
     g_signal_connect (toolbar, "notify::mode", G_CALLBACK (on_notify_mode),
                       NULL);
 }
diff --git a/src/gl-eventtoolbar.h b/src/gl-eventtoolbar.h
index c8c92d4..ef6e104 100644
--- a/src/gl-eventtoolbar.h
+++ b/src/gl-eventtoolbar.h
@@ -45,6 +45,8 @@ gboolean gl_event_toolbar_handle_back_button_event (GlEventToolbar *toolbar,
 void gl_event_toolbar_set_mode (GlEventToolbar *toolbar,
                                 GlEventToolbarMode mode);
 void gl_event_toolbar_add_boots (GlEventToolbar *toolbar, GArray *boot_ids);
+void gl_event_toolbar_change_current_boot (GlEventToolbar *toolbar,
+                                           const gchar *current_boot);
 
 G_END_DECLS
 
diff --git a/src/gl-eventview.c b/src/gl-eventview.c
index 425f24d..41c8a9e 100644
--- a/src/gl-eventview.c
+++ b/src/gl-eventview.c
@@ -58,6 +58,19 @@ static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
 static const gchar DESKTOP_SCHEMA[] = "org.gnome.desktop.interface";
 static const gchar CLOCK_FORMAT[] = "clock-format";
 
+gchar *
+gl_event_view_get_current_boot_time (GlEventView *view,
+                                     const gchar *boot_match)
+{
+    GlEventViewPrivate *priv;
+    GlEventViewList *events;
+
+    priv = gl_event_view_get_instance_private (view);
+    events = GL_EVENT_VIEW_LIST (priv->events);
+
+    return gl_event_view_list_get_current_boot_time (events, boot_match);
+}
+
 GArray *
 gl_event_view_get_boot_ids (GlEventView *view)
 {
diff --git a/src/gl-eventview.h b/src/gl-eventview.h
index ece1f4b..eb02ad4 100644
--- a/src/gl-eventview.h
+++ b/src/gl-eventview.h
@@ -51,6 +51,8 @@ void gl_event_view_set_search_mode (GlEventView *view, gboolean state);
 void gl_event_view_set_sort_order (GlEventView *view, GlSortOrder sort_order);
 GArray * gl_event_view_get_boot_ids (GlEventView *view);
 void gl_event_view_view_boot (GlEventView *view, const gchar *match);
+gchar * gl_event_view_get_current_boot_time (GlEventView *view,
+                                             const gchar *boot_match);
 
 G_END_DECLS
 
diff --git a/src/gl-eventviewlist.c b/src/gl-eventviewlist.c
index 5df55d5..614d0e8 100644
--- a/src/gl-eventviewlist.c
+++ b/src/gl-eventviewlist.c
@@ -478,6 +478,18 @@ gl_event_view_list_get_detail_entry (GlEventViewList *view)
     return priv->entry;
 }
 
+gchar *
+gl_event_view_list_get_current_boot_time (GlEventViewList *view,
+                                          const gchar *boot_match)
+{
+    GlEventViewListPrivate *priv;
+
+    priv = gl_event_view_list_get_instance_private (view);
+
+    return gl_journal_model_get_current_boot_time (priv->journal_model,
+                                                   boot_match);
+}
+
 GArray *
 gl_event_view_list_get_boot_ids (GlEventViewList *view)
 {
diff --git a/src/gl-eventviewlist.h b/src/gl-eventviewlist.h
index 02dc8da..3ba1083 100644
--- a/src/gl-eventviewlist.h
+++ b/src/gl-eventviewlist.h
@@ -39,6 +39,8 @@ void gl_event_view_list_set_search_mode (GlEventViewList *view, gboolean state);
 void gl_event_view_list_set_sort_order (GlEventViewList *view, GlSortOrder  sort_order);
 void gl_event_view_list_view_boot (GlEventViewList *view, const gchar *match);
 GArray * gl_event_view_list_get_boot_ids (GlEventViewList *view);
+gchar * gl_event_view_list_get_current_boot_time (GlEventViewList *view,
+                                                  const gchar *boot_match);
 
 G_END_DECLS
 
diff --git a/src/gl-journal-model.c b/src/gl-journal-model.c
index 3c53f31..e8b48e2 100644
--- a/src/gl-journal-model.c
+++ b/src/gl-journal-model.c
@@ -246,6 +246,13 @@ gl_journal_model_set_matches (GlJournalModel      *model,
     gl_journal_model_fetch_more_entries (model, FALSE);
 }
 
+gchar *
+gl_journal_model_get_current_boot_time (GlJournalModel *model,
+                                        const gchar *boot_match)
+{
+    return gl_journal_get_current_boot_time (model->journal, boot_match);
+}
+
 GArray *
 gl_journal_model_get_boot_ids (GlJournalModel *model)
 {
diff --git a/src/gl-journal-model.h b/src/gl-journal-model.h
index 1ebbb6e..c793b0a 100644
--- a/src/gl-journal-model.h
+++ b/src/gl-journal-model.h
@@ -36,4 +36,7 @@ void                    gl_journal_model_fetch_more_entries             (GlJourn
 
 GArray *                gl_journal_model_get_boot_ids                   (GlJournalModel *model);
 
+gchar *                 gl_journal_model_get_current_boot_time          (GlJournalModel *model,
+                                                                         const gchar *boot_match);
+
 #endif
diff --git a/src/gl-journal.c b/src/gl-journal.c
index 942dde6..a8dd044 100644
--- a/src/gl-journal.c
+++ b/src/gl-journal.c
@@ -18,6 +18,7 @@
  */
 
 #include "gl-journal.h"
+#include "gl-util.h"
 
 #include <glib-unix.h>
 #include <gio/gio.h>
@@ -65,13 +66,41 @@ gl_journal_error_quark (void)
     return g_quark_from_static_string ("gl-journal-error-quark");
 }
 
+gchar *
+gl_journal_get_current_boot_time (GlJournal *journal,
+                                  const gchar *boot_match)
+{
+    GArray *boot_ids;
+    gchar *time;
+    gint i;
+
+    boot_ids = gl_journal_get_boot_ids (journal);
+
+    for (i = boot_ids->len - 1; i >= boot_ids->len - 5 && i >= 0; i--)
+    {
+        GlJournalBootID *boot_id;
+
+        boot_id = &g_array_index (boot_ids, GlJournalBootID, i);
+
+        if (g_strcmp0 (boot_match, boot_id->boot_match) == 0)
+        {
+            time = gl_util_boot_time_to_display (boot_id->realtime_first,
+                                                 boot_id->realtime_last);
+
+            return time;
+        }
+    }
+
+    return NULL;
+}
+
 static gint
 boot_id_cmp (const void *a, const void *b)
 {
     guint64 _a, _b;
 
-    _a = ((const GlJournalBootID *)a)->realtime;
-    _b = ((const GlJournalBootID *)b)->realtime;
+    _a = ((const GlJournalBootID *)a)->realtime_first;
+    _b = ((const GlJournalBootID *)b)->realtime_first;
 
     return _a < _b ? -1 : (_a > _b ? 1 : 0);
 }
@@ -138,7 +167,34 @@ gl_journal_get_boots (GlJournal *journal)
             goto flush;
         }
 
-        r = sd_journal_get_realtime_usec (priv->journal, &boot_id.realtime);
+        r = sd_journal_get_realtime_usec (priv->journal,
+                                          &boot_id.realtime_first);
+        if (r < 0)
+        {
+            g_warning ("Error retrieving the sender timestamps: %s",
+                       g_strerror (-r));
+        }
+
+        r = sd_journal_seek_tail (priv->journal);
+        if (r < 0)
+        {
+            g_warning ("Error seeking to the beginning of the journal: %s\n",
+                       g_strerror (-r));
+        }
+
+        r = sd_journal_previous (priv->journal);
+        if (r < 0)
+        {
+            g_warning ("Error retreat the read pointer in the journal: %s",
+                       g_strerror (-r));
+        }
+        else if (r == 0)
+        {
+            goto flush;
+        }
+
+        r = sd_journal_get_realtime_usec (priv->journal,
+                                          &boot_id.realtime_last);
         if (r < 0)
         {
             g_warning ("Error retrieving the sender timestamps: %s",
diff --git a/src/gl-journal.h b/src/gl-journal.h
index 17bc67a..1e73480 100644
--- a/src/gl-journal.h
+++ b/src/gl-journal.h
@@ -62,7 +62,8 @@ typedef struct
 typedef struct
 {
     gchar *boot_match;
-    guint64 realtime;
+    guint64 realtime_first;
+    guint64 realtime_last;
 } GlJournalBootID;
 
 #define GL_TYPE_JOURNAL (gl_journal_get_type ())
@@ -74,6 +75,8 @@ void gl_journal_set_matches (GlJournal *journal, const gchar * const *matches);
 GArray * gl_journal_get_boot_ids (GlJournal *journal);
 GlJournalEntry * gl_journal_previous (GlJournal *journal);
 GlJournal * gl_journal_new (void);
+gchar * gl_journal_get_current_boot_time (GlJournal *journal,
+                                          const gchar *boot_match);
 
 guint64                 gl_journal_entry_get_timestamp                  (GlJournalEntry *entry);
 const gchar *           gl_journal_entry_get_message                    (GlJournalEntry *entry);
diff --git a/src/gl-util.c b/src/gl-util.c
index 40cea30..7c1372c 100644
--- a/src/gl-util.c
+++ b/src/gl-util.c
@@ -40,6 +40,9 @@ typedef enum
     GL_UTIL_TIMESTAMPS_DIFFERENT_YEAR
 } GlUtilTimestamps;
 
+static const gchar DESKTOP_SCHEMA[] = "org.gnome.desktop.interface";
+static const gchar CLOCK_FORMAT[] = "clock-format";
+
 /**
  * gl_util_on_css_provider_parsing_error:
  * @provider: the provider that had a parsing error
@@ -208,3 +211,38 @@ gl_util_get_uid (void)
 
     return uid;
 }
+
+gchar *
+gl_util_boot_time_to_display (guint64 realtime_first,
+                              guint64 realtime_last)
+{
+    gchar *time_first;
+    gchar *time_last;
+    gchar *time_display;
+    GDateTime *now;
+    GSettings *settings;
+    GlUtilClockFormat clock_format;
+
+    /* TODO: Monitor and propagate any GSettings changes. */
+    settings = g_settings_new (DESKTOP_SCHEMA);
+    clock_format = g_settings_get_enum (settings, CLOCK_FORMAT);
+
+    g_object_unref (settings);
+
+    now = g_date_time_new_now_local ();
+    time_first = gl_util_timestamp_to_display (realtime_first,
+                                               now, clock_format);
+    time_last = gl_util_timestamp_to_display (realtime_last,
+                                              now, clock_format);
+
+    /* Transltors: the first string is the earliest timestamp of the boot,
+     * and the second string is the newest timestamp. An example string might
+     * be '08:10 - 08:30' */
+    time_display = g_strdup_printf ("%s - %s", time_first, time_last);
+
+    g_date_time_unref (now);
+    g_free (time_first);
+    g_free (time_last);
+
+    return time_display;
+}
diff --git a/src/gl-util.h b/src/gl-util.h
index 3590128..4971564 100644
--- a/src/gl-util.h
+++ b/src/gl-util.h
@@ -43,6 +43,8 @@ gchar * gl_util_timestamp_to_display (guint64 microsecs,
                                       GDateTime *now,
                                       GlUtilClockFormat format);
 gint gl_util_get_uid (void);
+gchar * gl_util_boot_time_to_display (guint64 timestamp_first,
+                                      guint64 timestamp_last);
 
 G_END_DECLS
 
diff --git a/src/gl-window.c b/src/gl-window.c
index e7372b8..8dfccad 100644
--- a/src/gl-window.c
+++ b/src/gl-window.c
@@ -175,14 +175,29 @@ on_view_boot (GSimpleAction *action,
 {
     GlWindowPrivate *priv;
     GlEventView *event;
+    GlEventToolbar *toolbar;
+    gchar *current_boot;
     const gchar *boot_match;
 
     priv = gl_window_get_instance_private (GL_WINDOW (user_data));
     event = GL_EVENT_VIEW (priv->event);
+    toolbar = GL_EVENT_TOOLBAR (priv->event_toolbar);
 
     boot_match = g_variant_get_string (variant, NULL);
 
     gl_event_view_view_boot (event, boot_match);
+
+    current_boot = gl_event_view_get_current_boot_time (event, boot_match);
+    if (current_boot == NULL)
+    {
+        g_debug ("Error fetching the time using boot_match");
+    }
+
+    gl_event_toolbar_change_current_boot (toolbar, current_boot);
+
+    g_simple_action_set_state (action, variant);
+
+    g_free (current_boot);
 }
 
 static gboolean
@@ -259,7 +274,7 @@ static GActionEntry actions[] = {
     { "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 },
-    { "view-boot", on_view_boot, "s", NULL, NULL },
+    { "view-boot", on_action_radio, "s", "''", on_view_boot },
     { "close", on_close }
 };
 
@@ -287,7 +302,11 @@ gl_window_init (GlWindow *window)
     GlWindowPrivate *priv;
     GlEventToolbar *toolbar;
     GlEventView *event;
+    GAction *action_view_boot;
     GArray *boot_ids;
+    GlJournalBootID *boot_id;
+    gchar *boot_match;
+    GVariant *variant;
 
     gtk_widget_init_template (GTK_WIDGET (window));
 
@@ -296,11 +315,19 @@ gl_window_init (GlWindow *window)
     toolbar = GL_EVENT_TOOLBAR (priv->event_toolbar);
 
     boot_ids = gl_event_view_get_boot_ids (event);
+    boot_id = &g_array_index (boot_ids, GlJournalBootID, boot_ids->len - 1);
+    boot_match = boot_id->boot_match;
+
     gl_event_toolbar_add_boots (toolbar, boot_ids);
 
     g_action_map_add_action_entries (G_ACTION_MAP (window), actions,
                                      G_N_ELEMENTS (actions), window);
 
+    action_view_boot = g_action_map_lookup_action (G_ACTION_MAP (window),
+                                                   "view-boot");
+    variant = g_variant_new_string (boot_match);
+    g_action_change_state (action_view_boot, variant);
+
     provider = gtk_css_provider_new ();
     g_signal_connect (provider, "parsing-error",
                       G_CALLBACK (gl_util_on_css_provider_parsing_error),


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