/*
* 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-window.h"
#include
#include "gl-categorylist.h"
#include "gl-eventtoolbar.h"
#include "gl-eventviewlist.h"
#include "gl-enums.h"
#include "gl-util.h"
struct _GlWindow
{
/*< private >*/
GtkApplicationWindow parent_instance;
};
typedef struct
{
GtkWidget *event_toolbar;
GtkWidget *event_list;
GtkWidget *info_bar;
GtkLabel *message_label;
} GlWindowPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GlWindow, gl_window, ADW_TYPE_APPLICATION_WINDOW)
static const gchar SETTINGS_SCHEMA[] = "org.gnome.Logs";
static const gchar IGNORE_WARNING[] = "ignore-warning";
static void
on_action_radio (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
g_action_change_state (G_ACTION (action), variant);
}
static void
on_action_toggle (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
GVariant *variant_state;
gboolean state;
variant_state = g_action_get_state (G_ACTION (action));
state = g_variant_get_boolean (variant_state);
g_action_change_state (G_ACTION (action), g_variant_new_boolean (!state));
}
static void
on_close (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
GtkWindow *window;
window = GTK_WINDOW (user_data);
gtk_window_close (window);
}
static void
on_search (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
gboolean state;
GlWindowPrivate *priv;
GlEventViewList *event_list;
state = g_variant_get_boolean (variant);
priv = gl_window_get_instance_private (GL_WINDOW (user_data));
event_list = GL_EVENT_VIEW_LIST (priv->event_list);
gl_event_view_list_set_search_mode (event_list, state);
g_simple_action_set_state (action, variant);
}
static void
on_save_finish (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GlWindowPrivate *priv;
GlEventViewList *event_list;
GtkFileDialog *dialog;
gboolean have_error = FALSE;
gchar *file_content;
GFile *output_file;
GFileOutputStream *file_ostream;
GError *error = NULL;
GtkWidget *error_dialog;
priv = gl_window_get_instance_private (GL_WINDOW (user_data));
event_list = GL_EVENT_VIEW_LIST (priv->event_list);
dialog = GTK_FILE_DIALOG (source_object);
output_file = gtk_file_dialog_save_finish (dialog, res, &error);
if (error != NULL)
{
g_warning ("Error while replacing exported log messages file: %s",
error->message);
g_clear_error (&error);
return;
}
file_content = gl_event_view_list_get_output_logs (event_list);
file_ostream = g_file_replace (output_file, NULL, TRUE,
G_FILE_CREATE_NONE, NULL, &error);
if (error != NULL)
{
have_error = TRUE;
g_warning ("Error while replacing exported log messages file: %s",
error->message);
g_clear_error (&error);
}
/* Check against NULL pointer to avoid a crash when exporting and there
* are no log entries. */
if (file_content != NULL)
{
g_output_stream_write (G_OUTPUT_STREAM (file_ostream), file_content,
strlen (file_content), NULL, &error);
}
if (error != NULL)
{
have_error = TRUE;
g_warning ("Error while replacing exported log messages file: %s",
error->message);
g_clear_error (&error);
}
g_output_stream_close (G_OUTPUT_STREAM (file_ostream), NULL, &error);
if (error != NULL)
{
have_error = TRUE;
g_warning ("Error while replacing exported log messages file: %s",
error->message);
g_clear_error (&error);
}
if (have_error == TRUE)
{
error_dialog = adw_message_dialog_new (GTK_WINDOW (user_data),
_("Export Failed"),
_("Unable to export log messages to a file"));
adw_message_dialog_add_response (ADW_MESSAGE_DIALOG (error_dialog),
"close", _("_Close"));
adw_message_dialog_choose (ADW_MESSAGE_DIALOG (error_dialog),
NULL, NULL, NULL);
}
g_free (file_content);
g_object_unref (file_ostream);
g_object_unref (output_file);
}
static void
on_export (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
GtkFileDialog *dialog;
dialog = gtk_file_dialog_new ();
gtk_file_dialog_set_initial_name (dialog, _("log messages"));
gtk_file_dialog_save (dialog,
GTK_WINDOW (user_data),
NULL,
on_save_finish,
user_data);
}
static void
on_view_boot (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
GlWindowPrivate *priv;
GlEventViewList *event_list;
GlEventToolbar *toolbar;
GArray *boot_ids;
GlJournalBootID *boot_id;
const gchar *boot_match;
gchar *current_boot;
gchar *latest_boot;
priv = gl_window_get_instance_private (GL_WINDOW (user_data));
event_list = GL_EVENT_VIEW_LIST (priv->event_list);
toolbar = GL_EVENT_TOOLBAR (priv->event_toolbar);
boot_match = g_variant_get_string (variant, NULL);
gl_event_view_list_view_boot (event_list, boot_match);
current_boot = gl_event_view_list_get_boot_time (event_list, boot_match);
if (current_boot == NULL)
{
g_debug ("Error fetching the time using boot_match");
}
boot_ids = gl_event_view_list_get_boot_ids (event_list);
boot_id = &g_array_index (boot_ids, GlJournalBootID,
boot_ids->len - 1);
latest_boot = gl_event_view_list_get_boot_time (event_list,
boot_id->boot_match);
gl_event_toolbar_change_current_boot (toolbar, current_boot, latest_boot);
g_simple_action_set_state (action, variant);
g_free (current_boot);
g_free (latest_boot);
}
static void
on_category_list_changed (GlCategoryList *list,
GParamSpec *pspec,
gpointer user_data)
{
GlWindowPrivate *priv;
GlEventViewList *event_list;
GlEventToolbar *toolbar;
gchar *current_boot;
const gchar *boot_match;
priv = gl_window_get_instance_private (GL_WINDOW (user_data));
event_list = GL_EVENT_VIEW_LIST (priv->event_list);
toolbar = GL_EVENT_TOOLBAR (priv->event_toolbar);
boot_match = gl_event_view_list_get_boot_match (event_list);
current_boot = gl_event_view_list_get_boot_time (event_list, boot_match);
gl_event_toolbar_change_current_boot (toolbar, current_boot, current_boot);
g_free (current_boot);
}
static void
on_help_button_clicked (GlWindow *window,
gint response_id,
gpointer user_data)
{
GlWindowPrivate *priv;
GtkWidget *error_dialog;
g_autoptr (GError) error = NULL;
priv = gl_window_get_instance_private (GL_WINDOW (window));
g_app_info_launch_default_for_uri ("help:gnome-logs/permissions",
NULL, &error);
if (error != NULL)
{
error_dialog = adw_message_dialog_new (GTK_WINDOW (window),
_("Failed To Open Help"),
NULL);
adw_message_dialog_format_body (ADW_MESSAGE_DIALOG (error_dialog),
_("Failed to open the given help URI: %s"),
error->message);
adw_message_dialog_add_response (ADW_MESSAGE_DIALOG (error_dialog),
"close", _("_Close"));
adw_message_dialog_choose (ADW_MESSAGE_DIALOG (error_dialog),
NULL, NULL, NULL);
}
gtk_widget_set_visible (priv->info_bar, FALSE);
}
static void
on_ignore_button_clicked (GlWindow *window,
gint response_id,
gpointer user_data)
{
GlWindowPrivate *priv;
GSettings *settings;
priv = gl_window_get_instance_private (GL_WINDOW (window));
settings = g_settings_new (SETTINGS_SCHEMA);
g_settings_set_boolean (settings, IGNORE_WARNING, TRUE);
gtk_widget_set_visible (priv->info_bar, FALSE);
g_object_unref (settings);
}
void
gl_window_set_sort_order (GlWindow *window,
GlSortOrder sort_order)
{
GlWindowPrivate *priv;
GlEventViewList *event_list;
g_return_if_fail (GL_WINDOW (window));
priv = gl_window_get_instance_private (window);
event_list = GL_EVENT_VIEW_LIST (priv->event_list);
gl_event_view_list_set_sort_order (event_list, sort_order);
}
static GActionEntry actions[] = {
{ "search", on_action_toggle, NULL, "false", on_search },
{ "view-boot", on_action_radio, "s", "''", on_view_boot },
{ "export", on_export },
{ "close", on_close }
};
static void
gl_window_class_init (GlWindowClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/Logs/gl-window.ui");
gtk_widget_class_bind_template_child_private (widget_class, GlWindow,
event_toolbar);
gtk_widget_class_bind_template_child_private (widget_class, GlWindow,
event_list);
gtk_widget_class_bind_template_child_private (widget_class, GlWindow,
info_bar);
gtk_widget_class_bind_template_callback (widget_class,
on_help_button_clicked);
gtk_widget_class_bind_template_callback (widget_class,
on_ignore_button_clicked);
}
void
disable_export (GlWindow *window)
{
GAction *action_export;
action_export = g_action_map_lookup_action (G_ACTION_MAP (window),
"export");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action_export), FALSE);
}
void
enable_export (GlWindow *window)
{
GAction *action_export;
action_export = g_action_map_lookup_action (G_ACTION_MAP (window),
"export");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action_export), TRUE);
}
void
gl_window_load_journal (GlWindow *window,
GlJournal *journal)
{
gchar *boot_match;
GAction *action_view_boot;
GArray *boot_ids;
GVariant *variant;
GlJournalBootID *boot_id;
GlEventViewList *event_list;
GlEventToolbar *toolbar;
GlJournalModel *journal_model;
GlWindowPrivate *priv;
priv = gl_window_get_instance_private (window);
toolbar = GL_EVENT_TOOLBAR (priv->event_toolbar);
event_list = GL_EVENT_VIEW_LIST (priv->event_list);
journal_model = gl_event_view_list_get_journal_model (event_list);
gl_journal_model_load_journal (journal_model, journal);
boot_ids = gl_event_view_list_get_boot_ids (event_list);
gl_event_toolbar_add_boots (toolbar, boot_ids);
if (boot_ids->len > 0)
{
boot_id = &g_array_index (boot_ids, GlJournalBootID,
boot_ids->len - 1);
boot_match = boot_id->boot_match;
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);
}
}
static void
gl_window_init (GlWindow *window)
{
GtkCssProvider *provider;
GdkDisplay *display;
GlWindowPrivate *priv;
GlEventViewList *event_list;
GtkWidget *categories;
GlJournalStorage storage_type;
GSettings *settings;
gboolean ignore;
GlJournalModel *model;
/* Ensure these types that are used by the template have been
* registered before calling gtk_widget_init_template().
*/
g_type_ensure(GL_TYPE_CATEGORY_LIST);
g_type_ensure(GL_TYPE_EVENT_TOOLBAR);
g_type_ensure(GL_TYPE_EVENT_VIEW_LIST);
gtk_widget_init_template (GTK_WIDGET (window));
priv = gl_window_get_instance_private (window);
event_list = GL_EVENT_VIEW_LIST (priv->event_list);
g_action_map_add_action_entries (G_ACTION_MAP (window), actions,
G_N_ELEMENTS (actions), window);
categories = gl_event_view_list_get_category_list (event_list);
g_signal_connect (GL_CATEGORY_LIST (categories), "notify::category",
G_CALLBACK (on_category_list_changed), window);
model = gl_event_view_list_get_journal_model (event_list);
g_signal_connect_swapped (model, "disable_export",
G_CALLBACK (disable_export), window);
g_signal_connect_swapped (model, "enable_export",
G_CALLBACK (enable_export), window);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider,
"/org/gnome/Logs/gl-style.css");
display = gdk_display_get_default ();
gtk_style_context_add_provider_for_display (display,
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
settings = g_settings_new (SETTINGS_SCHEMA);
ignore = g_settings_get_boolean (settings, IGNORE_WARNING);
/* Don't show info_bar again if users have ever ignored the warning. */
if (ignore)
{
g_object_unref (provider);
g_object_unref (settings);
return;
}
/* Show warnings based on storage type. */
storage_type = gl_util_journal_storage_type ();
switch (storage_type)
{
case GL_JOURNAL_STORAGE_PERSISTENT:
{
if (!gl_util_can_read_system_journal (GL_JOURNAL_STORAGE_PERSISTENT))
{
gtk_label_set_label (priv->message_label, _("Unable to read system logs"));
gtk_widget_set_visible (priv->info_bar, TRUE);
}
if (!gl_util_can_read_user_journal ())
{
gtk_label_set_label (priv->message_label, _("Unable to read user logs"));
gtk_widget_set_visible (priv->info_bar, TRUE);
}
break;
}
case GL_JOURNAL_STORAGE_VOLATILE:
{
if (!gl_util_can_read_system_journal (GL_JOURNAL_STORAGE_VOLATILE))
{
gtk_label_set_label (priv->message_label, _("Unable to read system logs"));
gtk_widget_set_visible (priv->info_bar, TRUE);
}
break;
}
case GL_JOURNAL_STORAGE_NONE:
{
gtk_label_set_label (priv->message_label, _("No logs available"));
gtk_widget_set_visible (priv->info_bar, TRUE);
break;
}
default:
g_assert_not_reached ();
}
g_object_unref (provider);
g_object_unref (settings);
}
GtkWidget *
gl_window_new (GtkApplication *application)
{
g_return_val_if_fail (GTK_APPLICATION (application), NULL);
return g_object_new (GL_TYPE_WINDOW, "application", application, NULL);
}