/*
* GNOME Logs - View and search logs
* Copyright (C) 2013, 2014, 2015 Red Hat, Inc.
*
* 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 .
*/
#include "gl-eventviewrow.h"
#include
#include
#include
#include "gl-enums.h"
enum
{
PROP_0,
PROP_CATEGORY,
PROP_CLOCK_FORMAT,
PROP_ENTRY,
N_PROPERTIES
};
struct _GlEventViewRow
{
/*< private >*/
GtkListBoxRow parent_instance;
};
typedef struct
{
GlEventViewRowCategory category;
GlUtilClockFormat clock_format;
GlJournalEntry *entry;
GtkWidget *category_label;
GtkWidget *message_label;
GtkWidget *time_label;
} GlEventViewRowPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GlEventViewRow, gl_event_view_row, GTK_TYPE_LIST_BOX_ROW)
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
const gchar *
gl_event_view_row_get_command_line (GlEventViewRow *row)
{
GlEventViewRowPrivate *priv;
priv = gl_event_view_row_get_instance_private (row);
return gl_journal_entry_get_command_line (priv->entry);
}
guint64
gl_event_view_row_get_timestamp (GlEventViewRow *row)
{
GlEventViewRowPrivate *priv;
priv = gl_event_view_row_get_instance_private (row);
return gl_journal_entry_get_timestamp (priv->entry);
}
const gchar *
gl_event_view_row_get_message (GlEventViewRow *row)
{
GlEventViewRowPrivate *priv;
priv = gl_event_view_row_get_instance_private (row);
return gl_journal_entry_get_message (priv->entry);
}
GtkWidget *
gl_event_view_row_get_category_label (GlEventViewRow *row)
{
GlEventViewRowPrivate *priv;
priv = gl_event_view_row_get_instance_private (row);
return priv->category_label;
}
GtkWidget *
gl_event_view_row_get_message_label (GlEventViewRow *row)
{
GlEventViewRowPrivate *priv;
priv = gl_event_view_row_get_instance_private (row);
return priv->message_label;
}
GtkWidget *
gl_event_view_row_get_time_label (GlEventViewRow *row)
{
GlEventViewRowPrivate *priv;
priv = gl_event_view_row_get_instance_private (row);
return priv->time_label;
}
static void
gl_event_view_row_finalize (GObject *object)
{
GlEventViewRow *row = GL_EVENT_VIEW_ROW (object);
GlEventViewRowPrivate *priv = gl_event_view_row_get_instance_private (row);
g_clear_object (&priv->entry);
}
static void
gl_event_view_row_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GlEventViewRow *row = GL_EVENT_VIEW_ROW (object);
GlEventViewRowPrivate *priv = gl_event_view_row_get_instance_private (row);
switch (prop_id)
{
case PROP_CATEGORY:
g_value_set_enum (value, priv->category);
break;
case PROP_CLOCK_FORMAT:
g_value_set_enum (value, priv->clock_format);
break;
case PROP_ENTRY:
g_value_set_object (value, priv->entry);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gl_event_view_row_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GlEventViewRow *row = GL_EVENT_VIEW_ROW (object);
GlEventViewRowPrivate *priv = gl_event_view_row_get_instance_private (row);
switch (prop_id)
{
case PROP_CATEGORY:
priv->category = g_value_get_enum (value);
break;
case PROP_CLOCK_FORMAT:
priv->clock_format = g_value_get_enum (value);
break;
case PROP_ENTRY:
priv->entry = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gl_event_view_row_construct_category_label (GlEventViewRow *row,
GlJournalEntry *entry)
{
gint uid;
GlEventViewRowPrivate *priv;
const gchar *entry_uid_string;
gint entry_uid;
entry_uid_string = gl_journal_entry_get_uid (entry);
entry_uid = entry_uid_string ? atoi (entry_uid_string) : -1;
uid = gl_util_get_uid ();
priv = gl_event_view_row_get_instance_private (row);
/* The priority given to the categories should be determined by how
* specific the checks are. The applications category is the most
* specific, followed by the hardware category, then kernel, security
* and finally the least-specific other category. So we check the category
* in the order of applications, hardware, system, security and other. */
if ((g_strcmp0 (gl_journal_entry_get_transport (entry), "kernel") == 0
|| g_strcmp0 (gl_journal_entry_get_transport (entry), "stdout") == 0
|| g_strcmp0 (gl_journal_entry_get_transport (entry), "syslog") == 0)
&& entry_uid == uid)
{
priv->category_label = gtk_label_new (_("Applications"));
}
else if (g_strcmp0 (gl_journal_entry_get_transport (entry), "kernel") == 0
&& gl_journal_entry_get_kernel_device (entry) != NULL)
{
priv->category_label = gtk_label_new (_("Hardware"));
}
else if (g_strcmp0 (gl_journal_entry_get_transport (entry), "kernel") == 0)
{
priv->category_label = gtk_label_new (_("System"));
}
else if (gl_journal_entry_get_audit_session (entry) != NULL)
{
priv->category_label = gtk_label_new (_("Security"));
}
else
{
priv->category_label = gtk_label_new (_("Other"));
}
}
static gchar *
gl_event_view_row_replace_newline (const gchar *message)
{
GString *newmessage;
const gchar *newline_index;
const gchar *prevpos;
newmessage = g_string_sized_new (strlen (message));
prevpos = message;
newline_index = strchr (message, '\n');
while (newline_index != NULL)
{
g_string_append_len (newmessage, prevpos, newline_index - prevpos);
g_string_append_unichar (newmessage, 0x2424);
prevpos = newline_index + 1;
newline_index = strchr (prevpos, '\n');
}
g_string_append (newmessage, prevpos);
return g_string_free (newmessage, FALSE);
}
static void
gl_event_view_row_constructed (GObject *object)
{
GtkStyleContext *context;
GtkWidget *grid;
gchar *time;
const gchar *message;
gchar *newline_index;
gboolean rtl;
GlEventViewRowCategory category;
GlUtilClockFormat tmp_clock_format;
GlJournalEntry *tmp_entry;
GlJournalEntry *entry;
GDateTime *now;
GlEventViewRow *row = GL_EVENT_VIEW_ROW (object);
GlEventViewRowPrivate *priv;
priv = gl_event_view_row_get_instance_private (row);
entry = priv->entry;
rtl = (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL);
context = gtk_widget_get_style_context (GTK_WIDGET (row));
gtk_style_context_add_class (context, "event");
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_container_add (GTK_CONTAINER (row), grid);
g_object_get (object,
"category", &category,
"clock-format", &tmp_clock_format,
"entry", &tmp_entry,
NULL);
if (category == GL_EVENT_VIEW_ROW_CATEGORY_IMPORTANT)
{
gl_event_view_row_construct_category_label (row, entry);
context = gtk_widget_get_style_context (GTK_WIDGET (priv->category_label));
gtk_style_context_add_class (context, "dim-label");
gtk_style_context_add_class (context, "event-monospace");
gtk_label_set_xalign (GTK_LABEL (priv->category_label), 0);
gtk_grid_attach (GTK_GRID (grid), priv->category_label,
rtl ? 2 : 0, 0, 1, 1);
}
message = gl_journal_entry_get_message (entry);
newline_index = strchr (message, '\n');
if (newline_index)
{
gchar *message_label;
message_label = gl_event_view_row_replace_newline (message);
priv->message_label = gtk_label_new (message_label);
g_free (message_label);
}
else
{
priv->message_label = gtk_label_new (message);
}
gtk_widget_set_direction (priv->message_label, GTK_TEXT_DIR_LTR);
context = gtk_widget_get_style_context (GTK_WIDGET (priv->message_label));
gtk_style_context_add_class (context, "event-monospace");
gtk_widget_set_halign (priv->message_label, GTK_ALIGN_START);
gtk_label_set_ellipsize (GTK_LABEL (priv->message_label),
PANGO_ELLIPSIZE_END);
gtk_label_set_xalign (GTK_LABEL (priv->message_label), 0);
gtk_label_set_single_line_mode (GTK_LABEL (priv->message_label), TRUE);
gtk_grid_attach (GTK_GRID (grid), priv->message_label,
1, 0, 1, 1);
now = g_date_time_new_now_local ();
time = gl_util_timestamp_to_display (gl_journal_entry_get_timestamp (entry),
now, priv->clock_format, FALSE);
g_date_time_unref (now);
priv->time_label = gtk_label_new (time);
context = gtk_widget_get_style_context (GTK_WIDGET (priv->time_label));
gtk_style_context_add_class (context, "dim-label");
gtk_style_context_add_class (context, "event-monospace");
gtk_style_context_add_class (context, "event-time");
gtk_widget_set_halign (priv->time_label, GTK_ALIGN_END);
gtk_widget_set_hexpand (priv->time_label, TRUE);
gtk_label_set_xalign (GTK_LABEL (priv->time_label), 1);
gtk_grid_attach (GTK_GRID (grid), priv->time_label, rtl ? 0 : 2, 0, 1, 1);
g_free (time);
g_object_unref (tmp_entry);
gtk_widget_set_tooltip_text (GTK_WIDGET (row),
gl_journal_entry_get_message (entry));
gtk_widget_show_all (GTK_WIDGET (row));
G_OBJECT_CLASS (gl_event_view_row_parent_class)->constructed (object);
}
static void
gl_event_view_row_class_init (GlEventViewRowClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructed = gl_event_view_row_constructed;
gobject_class->finalize = gl_event_view_row_finalize;
gobject_class->get_property = gl_event_view_row_get_property;
gobject_class->set_property = gl_event_view_row_set_property;
obj_properties[PROP_CATEGORY] = g_param_spec_enum ("category", "Category",
"Filter rows from important category",
GL_TYPE_EVENT_VIEW_ROW_CATEGORY,
GL_EVENT_VIEW_ROW_CATEGORY_NONE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_CLOCK_FORMAT] = g_param_spec_enum ("clock-format", "Clock format",
"Format of the clock in which to show timestamps",
GL_TYPE_UTIL_CLOCK_FORMAT,
GL_UTIL_CLOCK_FORMAT_24HR,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_ENTRY] = g_param_spec_object ("entry", "Entry",
"Journal entry for this row",
GL_TYPE_JOURNAL_ENTRY,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPERTIES,
obj_properties);
}
static void
gl_event_view_row_init (GlEventViewRow *row)
{
/* The widgets are initialized in gl_event_view_row_constructed (), because
* at _init() time the construct-only properties have not been set. */
}
GlJournalEntry *
gl_event_view_row_get_entry (GlEventViewRow *row)
{
GlEventViewRowPrivate *priv;
g_return_val_if_fail (GL_EVENT_VIEW_ROW (row), NULL);
priv = gl_event_view_row_get_instance_private (row);
return priv->entry;
}
GtkWidget *
gl_event_view_row_new (GlJournalEntry *entry,
GlUtilClockFormat clock_format,
GlEventViewRowCategory category)
{
return g_object_new (GL_TYPE_EVENT_VIEW_ROW, "entry", entry,
"clock-format", clock_format,
"category", category, NULL);
}