/* * GNOME Logs - View and search logs * Copyright (C) 2016 Pranav Ganorkar * * 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-searchpopover.h" #include "gl-enums.h" #include "gl-util.h" #include #include struct _GlSearchPopover { /*< private >*/ GtkPopover parent_instance; }; typedef struct { /* Search popover elements */ GtkWidget *parameter_stack; GtkWidget *parameter_button_label; GtkWidget *parameter_label_stack; GtkWidget *parameter_treeview; GtkListStore *parameter_liststore; GtkWidget *search_type_revealer; GtkWidget *range_stack; GtkWidget *range_label_stack; GtkWidget *range_treeview; GtkWidget *range_button_label; GtkWidget *clear_range_button; GtkWidget *range_button_drop_down_image; GtkListStore *range_liststore; GtkWidget *menu_stack; GtkWidget *start_date_stack; GtkWidget *start_date_calendar_revealer; GtkWidget *start_date_entry; GtkWidget *start_date_button_label; GtkWidget *start_time_stack; GtkWidget *start_time_hour_spin; GtkWidget *start_time_minute_spin; GtkWidget *start_time_second_spin; GtkWidget *start_time_period_spin; GtkWidget *start_time_button_label; GtkWidget *start_time_period_label; GtkWidget *end_date_stack; GtkWidget *end_date_calendar_revealer; GtkWidget *end_date_entry; GtkWidget *end_date_button_label; GtkWidget *end_time_stack; GtkWidget *end_time_hour_spin; GtkWidget *end_time_minute_spin; GtkWidget *end_time_second_spin; GtkWidget *end_time_period_spin; GtkWidget *end_time_button_label; GtkWidget *end_time_period_label; GlSearchPopoverJournalFieldFilter journal_search_field; GlQuerySearchType search_type; GlSearchPopoverJournalTimestampRange journal_timestamp_range; guint64 custom_start_timestamp; guint64 custom_end_timestamp; GlUtilClockFormat clock_format; } GlSearchPopoverPrivate; enum { PROP_0, PROP_JOURNAL_SEARCH_FIELD, PROP_SEARCH_TYPE, PROP_JOURNAL_TIMESTAMP_RANGE, N_PROPERTIES }; enum { COLUMN_JOURNAL_FIELD_LABEL, COLUMN_JOURNAL_FIELD_SHOW_SEPARATOR, COLUMN_JOURNAL_FIELD_ENUM_VALUE, JOURNAL_FIELD_N_COLUMNS }; enum { COLUMN_JOURNAL_TIMESTAMP_RANGE_LABEL, COLUMN_JOURNAL_TIMESTAMP_RANGE_SHOW_SEPARATOR, COLUMN_JOURNAL_TIMESTAMP_RANGE_ENUM_VALUE, JOURNAL_TIMESTAMP_RANGE_N_COLUMNS }; enum { AM, PM }; static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; static const gchar DESKTOP_SCHEMA[] = "org.gnome.desktop.interface"; static const gchar CLOCK_FORMAT[] = "clock-format"; G_DEFINE_TYPE_WITH_PRIVATE (GlSearchPopover, gl_search_popover, GTK_TYPE_POPOVER) /* Event handlers for search popover elements */ static void search_popover_closed (GtkPopover *popover, gpointer user_data) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (GL_SEARCH_POPOVER (user_data)); gtk_stack_set_visible_child_name (GTK_STACK (priv->parameter_stack), "parameter-button"); gtk_stack_set_visible_child_name (GTK_STACK (priv->parameter_label_stack), "what-label"); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_stack), "range-button"); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_label_stack), "when-label"); } static void select_parameter_button_clicked (GtkButton *button, gpointer user_data) { GlSearchPopoverPrivate *priv; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; gboolean valid; priv = gl_search_popover_get_instance_private (GL_SEARCH_POPOVER (user_data)); gtk_stack_set_visible_child_name (GTK_STACK (priv->parameter_stack), "parameter-list"); gtk_stack_set_visible_child_name (GTK_STACK (priv->parameter_label_stack), "select-parameter-label"); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_stack), "range-button"); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_label_stack), "when-label"); model = GTK_TREE_MODEL (priv->parameter_liststore); valid = gtk_tree_model_get_iter_first (model, &iter); while (valid) { GlSearchPopoverJournalFieldFilter journal_field_enum_value; gtk_tree_model_get (GTK_TREE_MODEL (priv->parameter_liststore), &iter, COLUMN_JOURNAL_FIELD_ENUM_VALUE, &journal_field_enum_value, -1); if (priv->journal_search_field == journal_field_enum_value) { break; } valid = gtk_tree_model_iter_next (model, &iter); } selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->parameter_treeview)); gtk_tree_selection_select_iter (selection, &iter); } static gboolean parameter_treeview_row_seperator (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) { GlSearchPopover *popover; GlSearchPopoverPrivate *priv; gboolean show_separator; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); gtk_tree_model_get (GTK_TREE_MODEL (priv->parameter_liststore), iter, COLUMN_JOURNAL_FIELD_SHOW_SEPARATOR, &show_separator, -1); return show_separator; } static void on_parameter_treeview_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) { GtkTreeIter iter; GlSearchPopover *popover; GlSearchPopoverPrivate *priv; gchar *journal_field_label; GlSearchPopoverJournalFieldFilter journal_field_enum_value; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->parameter_liststore), &iter, path); gtk_tree_model_get (GTK_TREE_MODEL (priv->parameter_liststore), &iter, COLUMN_JOURNAL_FIELD_LABEL, &journal_field_label, COLUMN_JOURNAL_FIELD_ENUM_VALUE, &journal_field_enum_value, -1); gtk_label_set_label (GTK_LABEL (priv->parameter_button_label), _(journal_field_label)); priv->journal_search_field = journal_field_enum_value; g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_SEARCH_FIELD]); /* Do not Show "Search Type" option if all available fields group is selected */ if (priv->journal_search_field == GL_SEARCH_POPOVER_JOURNAL_FIELD_FILTER_ALL_AVAILABLE_FIELDS) { gtk_revealer_set_reveal_child (GTK_REVEALER (priv->search_type_revealer), FALSE); gtk_widget_set_visible (priv->search_type_revealer, FALSE); } else { gtk_widget_set_visible (priv->search_type_revealer, TRUE); gtk_revealer_set_reveal_child (GTK_REVEALER (priv->search_type_revealer), TRUE); } gtk_stack_set_visible_child_name (GTK_STACK (priv->parameter_stack), "parameter-button"); gtk_stack_set_visible_child_name (GTK_STACK (priv->parameter_label_stack), "what-label"); g_free (journal_field_label); } static void search_type_changed (GtkCheckButton *check_button, gpointer user_data) { GlSearchPopover *popover; GlSearchPopoverPrivate *priv; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); if (gtk_check_button_get_active (check_button)) { priv->search_type = GL_QUERY_SEARCH_TYPE_EXACT; } else { priv->search_type = GL_QUERY_SEARCH_TYPE_SUBSTRING; } /* Inform GlEventViewlist about search type property change */ g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_SEARCH_TYPE]); } GlSearchPopoverJournalFieldFilter gl_search_popover_get_journal_search_field (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); return priv->journal_search_field; } GlQuerySearchType gl_search_popover_get_query_search_type (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); return priv->search_type; } GlSearchPopoverJournalTimestampRange gl_search_popover_get_journal_timestamp_range (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); return priv->journal_timestamp_range; } static void gl_search_popover_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GlSearchPopover *popover = GL_SEARCH_POPOVER (object); GlSearchPopoverPrivate *priv = gl_search_popover_get_instance_private (popover); switch (prop_id) { case PROP_JOURNAL_SEARCH_FIELD: g_value_set_enum (value, priv->journal_search_field); break; case PROP_SEARCH_TYPE: g_value_set_enum (value, priv->search_type); break; case PROP_JOURNAL_TIMESTAMP_RANGE: g_value_set_enum (value, priv->journal_timestamp_range); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gl_search_popover_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GlSearchPopover *popover = GL_SEARCH_POPOVER (object); GlSearchPopoverPrivate *priv = gl_search_popover_get_instance_private (popover); switch (prop_id) { case PROP_JOURNAL_SEARCH_FIELD: priv->journal_search_field = g_value_get_enum (value); break; case PROP_SEARCH_TYPE: priv->search_type = g_value_get_enum (value); break; case PROP_JOURNAL_TIMESTAMP_RANGE: priv->journal_timestamp_range = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void select_range_button_clicked (GtkButton *button, gpointer user_data) { GlSearchPopoverPrivate *priv; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; gboolean valid; priv = gl_search_popover_get_instance_private (GL_SEARCH_POPOVER (user_data)); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_stack), "range-list"); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_label_stack), "show-log-from-label"); gtk_stack_set_visible_child_name (GTK_STACK (priv->parameter_stack), "parameter-button"); gtk_stack_set_visible_child_name (GTK_STACK (priv->parameter_label_stack), "what-label"); model = GTK_TREE_MODEL (priv->range_liststore); valid = gtk_tree_model_get_iter_first (model, &iter); while (valid) { GlSearchPopoverJournalTimestampRange journal_range_enum_value; gtk_tree_model_get (GTK_TREE_MODEL (priv->range_liststore), &iter, COLUMN_JOURNAL_TIMESTAMP_RANGE_ENUM_VALUE, &journal_range_enum_value, -1); if (priv->journal_timestamp_range == journal_range_enum_value) { break; } valid = gtk_tree_model_iter_next (model, &iter); } selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->range_treeview)); gtk_tree_selection_select_iter (selection, &iter); } static gboolean range_treeview_row_seperator (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) { GlSearchPopover *popover; GlSearchPopoverPrivate *priv; gboolean show_seperator; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); gtk_tree_model_get (GTK_TREE_MODEL (priv->range_liststore), iter, COLUMN_JOURNAL_TIMESTAMP_RANGE_SHOW_SEPARATOR, &show_seperator, -1); return show_seperator; } static void show_start_date_widgets (GlSearchPopover *popover, gboolean visible) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); gtk_stack_set_visible_child_name (GTK_STACK (priv->start_date_stack), visible ? "start-date-entry" : "start-date-button"); gtk_widget_set_visible (priv->start_date_calendar_revealer, visible); gtk_revealer_set_reveal_child (GTK_REVEALER (priv->start_date_calendar_revealer), visible); } static void show_start_time_widgets (GlSearchPopover *popover, gboolean visible) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); gtk_stack_set_visible_child_name (GTK_STACK (priv->start_time_stack), visible ? "start-time-spinbutton" : "start-time-select-button"); } static void show_end_date_widgets (GlSearchPopover *popover, gboolean visible) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); gtk_stack_set_visible_child_name (GTK_STACK (priv->end_date_stack), visible ? "end-date-entry" : "end-date-button"); gtk_widget_set_visible (priv->end_date_calendar_revealer, visible); gtk_revealer_set_reveal_child (GTK_REVEALER (priv->end_date_calendar_revealer), visible); } static void show_end_time_widgets (GlSearchPopover *popover, gboolean visible) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); gtk_stack_set_visible_child_name (GTK_STACK (priv->end_time_stack), visible ? "end-time-spinbutton" : "end-time-select-button"); } static void reset_custom_range_widgets (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); priv->custom_start_timestamp = 0; priv->custom_end_timestamp = 0; /* Close any previously opened widgets in the submenu */ show_start_date_widgets (popover, FALSE); show_start_time_widgets (popover, FALSE); show_end_date_widgets (popover, FALSE); show_end_time_widgets (popover, FALSE); /* Reset start range elements */ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->start_time_minute_spin), 59.0); gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->start_time_second_spin), 59.0); if (priv->clock_format == GL_UTIL_CLOCK_FORMAT_12HR) { gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->start_time_hour_spin), 11.0); gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->start_time_period_spin), PM); } else { gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->start_time_hour_spin), 23.0); } gtk_editable_set_text (GTK_EDITABLE (priv->start_date_entry), ""); gtk_label_set_label (GTK_LABEL (priv->start_date_button_label), _("Select Start Date…")); gtk_label_set_label (GTK_LABEL (priv->start_time_button_label), _("Select Start Time…")); /*Reset end range elements */ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->end_time_minute_spin), 0.0); gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->end_time_second_spin), 0.0); if (priv->clock_format == GL_UTIL_CLOCK_FORMAT_12HR) { gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->end_time_hour_spin), 12.0); gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->end_time_period_spin), AM); } else { gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->end_time_hour_spin), 0.0); } gtk_editable_set_text (GTK_EDITABLE (priv->end_date_entry), ""); gtk_label_set_label (GTK_LABEL (priv->end_date_button_label), _("Select End Date…")); gtk_label_set_label (GTK_LABEL (priv->end_time_button_label), _("Select End Time…")); } static void on_range_treeview_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) { GtkTreeIter iter; gchar *range_label; GlSearchPopoverJournalTimestampRange journal_range_enum_value; GlSearchPopover *popover; GlSearchPopoverPrivate *priv; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->range_liststore), &iter, path); gtk_tree_model_get (GTK_TREE_MODEL (priv->range_liststore), &iter, COLUMN_JOURNAL_TIMESTAMP_RANGE_LABEL, &range_label, COLUMN_JOURNAL_TIMESTAMP_RANGE_ENUM_VALUE, &journal_range_enum_value, -1); priv->journal_timestamp_range = journal_range_enum_value; if (priv->journal_timestamp_range == GL_SEARCH_POPOVER_JOURNAL_TIMESTAMP_RANGE_CUSTOM) { gtk_stack_set_visible_child_name (GTK_STACK (priv->menu_stack), "custom-range-submenu"); } else { /* Reset the Custom Range elements if set as only one filter can be applied at time */ reset_custom_range_widgets (popover); gtk_label_set_label (GTK_LABEL (priv->range_button_label), _(range_label)); g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE]); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_stack), "range-button"); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_label_stack), "when-label"); } g_free (range_label); } static void start_date_button_clicked (GtkButton *button, gpointer user_data) { GlSearchPopover *popover = GL_SEARCH_POPOVER (user_data); show_start_date_widgets (popover, TRUE); show_start_time_widgets (popover, FALSE); show_end_date_widgets (popover, FALSE); show_end_time_widgets (popover, FALSE); } /* Utility function for converting hours from 12 hour format to 24 hour format */ static void convert_hour (gint *hour, gint time_period) { if (*hour == 12 && time_period == AM) { *hour = 0; } else if (*hour != 12 && time_period == PM) { *hour = *hour + 12; } } static GDateTime * get_start_date_time (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; const gchar *entry_date; GDateTime *now; GDateTime *start_date_time; GDate *start_date; gint hour; gint minute; gint second; priv = gl_search_popover_get_instance_private (popover); entry_date = gtk_editable_get_text (GTK_EDITABLE (priv->start_date_entry)); hour = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->start_time_hour_spin)); minute = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->start_time_minute_spin)); second = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->start_time_second_spin)); /* Convert to 24 Hour format */ if (priv->clock_format == GL_UTIL_CLOCK_FORMAT_12HR) { gint time_period; time_period = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->start_time_period_spin)); convert_hour (&hour, time_period); } /* Parse the date entered into the Text Entry */ start_date = g_date_new (); g_date_set_parse (start_date, entry_date); now = g_date_time_new_now_local (); /* If Invalid date, then take today's date as default */ if (!g_date_valid (start_date)) { start_date_time = g_date_time_new_local (g_date_time_get_year (now), g_date_time_get_month (now), g_date_time_get_day_of_month (now), hour, minute, second); } else { start_date_time = g_date_time_new_local (g_date_get_year (start_date), g_date_get_month (start_date), g_date_get_day (start_date), hour, minute, second); } g_date_time_unref (now); g_date_free (start_date); return start_date_time; } static void update_range_button (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; gchar *range_button_label; gchar *display_time; GDateTime *now; priv = gl_search_popover_get_instance_private (popover); now = g_date_time_new_now_local (); /* Update range button label according to timestamps set in the custom range submenu */ if (priv->custom_end_timestamp && priv->custom_start_timestamp) { range_button_label = gl_util_boot_time_to_display (priv->custom_start_timestamp, priv->custom_end_timestamp); } else if (priv->custom_start_timestamp && !priv->custom_end_timestamp) { display_time = gl_util_timestamp_to_display (priv->custom_start_timestamp, now, priv->clock_format, FALSE); /* Translators: if only custom start timestamp is set, then we update * the timestamp range button label in popover to show that * logs are shown in the window starting from this timestamp * until the ending timestamp of journal. */ range_button_label = g_strdup_printf (_("From %s"), display_time); g_free (display_time); } else { display_time = gl_util_timestamp_to_display (priv->custom_end_timestamp, now, priv->clock_format, FALSE); /* Translators: if only custom end timestamp is set, then we update * the timestamp range button label in popover to show that * logs are shown in the window upto this timestamp * with the starting timestamp being the current time. */ range_button_label = g_strdup_printf (_("Until %s"), display_time); g_free (display_time); } gtk_label_set_label (GTK_LABEL (priv->range_button_label), range_button_label); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_stack), "range-button"); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_label_stack), "when-label"); g_date_time_unref (now); g_free (range_button_label); } static void start_date_calendar_day_selected (GtkCalendar *calendar, gpointer user_data) { GDateTime *date; GDateTime *now; gchar *date_label; GlSearchPopover *popover; GlSearchPopoverPrivate *priv; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); date = gtk_calendar_get_date (calendar); /* Translators: date format for the start date entry * and start date button label in the custom range submenu, * showing the day of month in decimal number, full month * name as string, the year as a decimal number including the century. */ date_label = g_date_time_format (date, _("%e %B %Y")); now = g_date_time_new_now_local (); /* If a future date, fail silently */ if (g_date_time_compare (date, now) != 1) { GDateTime *start_date_time; gtk_editable_set_text (GTK_EDITABLE (priv->start_date_entry), date_label); gtk_label_set_label (GTK_LABEL (priv->start_date_button_label), date_label); start_date_time = get_start_date_time (popover); priv->custom_start_timestamp = g_date_time_to_unix (start_date_time) * G_USEC_PER_SEC; update_range_button (popover); g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE]); g_date_time_unref (start_date_time); } g_date_time_unref (date); } static void start_date_entry_activate (GtkEntry *entry, gpointer user_data) { GlSearchPopover *popover; GlSearchPopoverPrivate *priv; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); if (gtk_entry_get_text_length (entry) > 0) { GDateTime *now; GDateTime *date_time; GDate *date; date = g_date_new (); g_date_set_parse (date, gtk_editable_get_text (GTK_EDITABLE (entry))); /* Invalid date silently does nothing */ if (!g_date_valid (date)) { g_date_free (date); return; } now = g_date_time_new_now_local (); date_time = g_date_time_new_local (g_date_get_year (date), g_date_get_month (date), g_date_get_day (date), 0, 0, 0); /* Future dates silently fail */ if (g_date_time_compare (date_time, now) != 1) { GDateTime *start_date_time; gtk_label_set_label (GTK_LABEL (priv->start_date_button_label), gtk_editable_get_text (GTK_EDITABLE (entry))); show_start_date_widgets (popover, FALSE); start_date_time = get_start_date_time (popover); priv->custom_start_timestamp = g_date_time_to_unix (start_date_time) * G_USEC_PER_SEC; update_range_button (popover); g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE]); g_date_time_unref (start_date_time); } g_date_time_unref (now); g_date_time_unref (date_time); g_date_free (date); } } static void start_time_button_clicked (GtkButton *button, gpointer user_data) { GlSearchPopover *popover = GL_SEARCH_POPOVER (user_data); show_start_time_widgets (popover, TRUE); show_start_date_widgets (popover, FALSE); show_end_time_widgets (popover, FALSE); show_end_date_widgets (popover, FALSE); } static void roundoff_invalid_time_value (GtkSpinButton *spin_button, gdouble *new_val, gint lower_limit, gint upper_limit) { gint time; time = atoi (gtk_editable_get_text (GTK_EDITABLE (spin_button))); /* Roundoff to the nearest limit if out of limits*/ if (time < lower_limit) { *new_val = lower_limit; } else if (time > upper_limit) { *new_val = upper_limit; } else { *new_val = time; } } static gboolean spinbox_format_time_period_to_text (GtkSpinButton *spin_button, gpointer user_data) { gchar *time_period_string; gint time_period; time_period = gtk_spin_button_get_value_as_int (spin_button); if (time_period == AM) { time_period_string = g_strdup_printf (_("AM")); } else { time_period_string = g_strdup_printf (_("PM")); } gtk_editable_set_text (GTK_EDITABLE (spin_button), time_period_string); g_free (time_period_string); return TRUE; } static gint spinbox_format_time_period_to_int (GtkSpinButton *spin_button, gdouble *time_period, gpointer user_data) { const gchar *time_period_string; time_period_string = gtk_editable_get_text (GTK_EDITABLE (spin_button)); if ( g_strcmp0 ("PM", time_period_string) == 0) { *time_period = PM; } else { /* Reset invalid values to "AM" */ *time_period = AM; } return TRUE; } /* Common event handler for validating spinbutton entry values */ static gint spinbox_entry_validate_hour_min_sec (GtkSpinButton *spin_button, gdouble *new_val, gpointer user_data) { const gchar *spinbutton_id; GlSearchPopover *popover; GlSearchPopoverPrivate *priv; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); spinbutton_id = gtk_buildable_get_buildable_id (GTK_BUILDABLE (spin_button)); /* Check if called from hour spinboxes */ if (g_strcmp0 (spinbutton_id, "end_time_hour_spin") == 0 || g_strcmp0 (spinbutton_id, "start_time_hour_spin") == 0) { if (priv->clock_format == GL_UTIL_CLOCK_FORMAT_24HR) { roundoff_invalid_time_value (spin_button, new_val, 0, 23); } else { roundoff_invalid_time_value (spin_button, new_val, 1, 12); } } else { roundoff_invalid_time_value (spin_button, new_val, 0, 59); } return TRUE; } static gboolean spinbox_entry_format_two_digits (GtkSpinButton *spin_button, gpointer user_data) { gchar *time_string; gint value; value = gtk_spin_button_get_value_as_int (spin_button); time_string = g_strdup_printf ("%02d", value); gtk_editable_set_text (GTK_EDITABLE (spin_button), time_string); g_free (time_string); return TRUE; } /* Common event handler for setting query when any one of start time spinbutton changes */ static void start_time_spinbox_value_changed (GtkSpinButton *spin_button, gpointer user_data) { GlSearchPopover *popover; GlSearchPopoverPrivate *priv; GDateTime *start_date_time; gchar *button_label; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); start_date_time = get_start_date_time (popover); if (priv->clock_format == GL_UTIL_CLOCK_FORMAT_12HR) { /* Translators: timestamp format for the custom start time button * label in the custom range submenu, showing the time with seconds * in 12-hour format. */ button_label = g_date_time_format (start_date_time, _("%I:%M:%S %p")); } else { /* Translators: timestamp format for the custom start time button * label in the custom range submenu, showing the time with seconds * in 24-hour format. */ button_label = g_date_time_format (start_date_time, _("%T")); } gtk_label_set_label (GTK_LABEL (priv->start_time_button_label), button_label); priv->custom_start_timestamp = g_date_time_to_unix (start_date_time) * G_USEC_PER_SEC; update_range_button (popover); g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE]); g_date_time_unref (start_date_time); g_free (button_label); } static void end_date_button_clicked (GtkButton *button, gpointer user_data) { GlSearchPopover *popover = GL_SEARCH_POPOVER (user_data); show_end_date_widgets (popover, TRUE); show_end_time_widgets (popover, FALSE); show_start_date_widgets (popover, FALSE); show_start_time_widgets (popover, FALSE); } static GDateTime * get_end_date_time (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; const gchar *entry_date; GDateTime *now; GDateTime *end_date_time; GDate *end_date; gint hour; gint minute; gint second; priv = gl_search_popover_get_instance_private (popover); entry_date = gtk_editable_get_text (GTK_EDITABLE (priv->end_date_entry)); hour = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->end_time_hour_spin)); minute = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->end_time_minute_spin)); second = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->end_time_second_spin)); /* Convert to 24 Hour format */ if (priv->clock_format == GL_UTIL_CLOCK_FORMAT_12HR) { gint time_period; time_period = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->end_time_period_spin)); convert_hour (&hour, time_period); } /* Parse the date entered into the Text Entry */ end_date = g_date_new (); g_date_set_parse (end_date, entry_date); now = g_date_time_new_now_local (); /* If Invalid date, then take today's date as default */ if (!g_date_valid (end_date)) { end_date_time = g_date_time_new_local (g_date_time_get_year (now), g_date_time_get_month (now), g_date_time_get_day_of_month (now), hour, minute, second); } else { end_date_time = g_date_time_new_local (g_date_get_year (end_date), g_date_get_month (end_date), g_date_get_day (end_date), hour, minute, second); } g_date_time_unref (now); g_date_free (end_date); return end_date_time; } static void end_date_calendar_day_selected (GtkCalendar *calendar, gpointer user_data) { GDateTime *date; GDateTime *now; gchar *date_label; GlSearchPopover *popover; GlSearchPopoverPrivate *priv; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); date = gtk_calendar_get_date (calendar); /* Translators: date format for the end date entry * and end date button label in the custom range submenu, * showing the day of month in decimal number, full month * name as string, the year as a decimal number including the century. */ date_label = g_date_time_format (date, _("%e %B %Y")); now = g_date_time_new_now_local (); /* If a future date, fail silently */ if (g_date_time_compare (date, now) != 1) { GDateTime *end_date_time; gtk_editable_set_text (GTK_EDITABLE (priv->end_date_entry), date_label); gtk_label_set_label (GTK_LABEL (priv->end_date_button_label), date_label); end_date_time = get_end_date_time (popover); priv->custom_end_timestamp = g_date_time_to_unix (end_date_time) * G_USEC_PER_SEC; update_range_button (popover); g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE]); g_date_time_unref (end_date_time); } g_date_time_unref (date); } static void end_date_entry_activate (GtkEntry *entry, gpointer user_data) { GlSearchPopover *popover; GlSearchPopoverPrivate *priv; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); if (gtk_entry_get_text_length (entry) > 0) { GDateTime *now; GDateTime *date_time; GDate *date; date = g_date_new (); g_date_set_parse (date, gtk_editable_get_text (GTK_EDITABLE (entry))); /* Invalid date silently does nothing */ if (!g_date_valid (date)) { g_date_free (date); return; } now = g_date_time_new_now_local (); date_time = g_date_time_new_local (g_date_get_year (date), g_date_get_month (date), g_date_get_day (date), 0, 0, 0); /* Future dates silently fails */ if (g_date_time_compare (date_time, now) != 1) { GDateTime *end_date_time; gtk_label_set_label (GTK_LABEL (priv->end_date_button_label), gtk_editable_get_text (GTK_EDITABLE (entry))); show_end_date_widgets (popover, FALSE); end_date_time = get_end_date_time (popover); priv->custom_end_timestamp = g_date_time_to_unix (end_date_time) * G_USEC_PER_SEC; update_range_button (popover); g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE]); g_date_time_unref (end_date_time); } g_date_time_unref (now); g_date_time_unref (date_time); g_date_free (date); } } static void end_time_button_clicked (GtkButton *button, gpointer user_data) { GlSearchPopover *popover = GL_SEARCH_POPOVER (user_data); show_end_time_widgets (popover, TRUE); show_end_date_widgets (popover, FALSE); show_start_time_widgets (popover, FALSE); show_start_date_widgets (popover, FALSE); } static void end_time_spinbox_value_changed (GtkSpinButton *spin_button, gpointer user_data) { GlSearchPopover *popover; GlSearchPopoverPrivate *priv; GDateTime *end_date_time; gchar *button_label; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); end_date_time = get_end_date_time (popover); if (priv->clock_format == GL_UTIL_CLOCK_FORMAT_12HR) { /* Translators: timestamp format for the custom end time button * label in the custom range submenu, showing the time with seconds * in 12-hour format. */ button_label = g_date_time_format (end_date_time, _("%I:%M:%S %p")); } else { /* Translators: timestamp format for the custom end time button * label in the custom range submenu, showing the time with seconds * in 24-hour format. */ button_label = g_date_time_format (end_date_time, _("%T")); } gtk_label_set_label (GTK_LABEL (priv->end_time_button_label), button_label); priv->custom_end_timestamp = g_date_time_to_unix (end_date_time) * G_USEC_PER_SEC; update_range_button (popover); g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE]); g_date_time_unref (end_date_time); g_free (button_label); } static void custom_range_submenu_back_button_clicked (GtkButton *button, gpointer user_data) { GlSearchPopover *popover; GlSearchPopoverPrivate *priv; popover = GL_SEARCH_POPOVER (user_data); priv = gl_search_popover_get_instance_private (popover); /* Default to Current boot if none of the timestamp was set */ if (!priv->custom_start_timestamp && !priv->custom_end_timestamp) { gl_search_popover_set_journal_timestamp_range_current_boot (popover); } gtk_stack_set_visible_child_name (GTK_STACK (priv->range_stack), "range-button"); gtk_stack_set_visible_child_name (GTK_STACK (priv->range_label_stack), "when-label"); gtk_stack_set_visible_child_name (GTK_STACK (priv->menu_stack), "main"); } guint64 gl_search_popover_get_custom_start_timestamp (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); return priv->custom_start_timestamp; } guint64 gl_search_popover_get_custom_end_timestamp (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); return priv->custom_end_timestamp; } void gl_search_popover_set_journal_timestamp_range_current_boot (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; priv = gl_search_popover_get_instance_private (popover); /* Reset the Custom Range elements if set as only one filter can be applied at time */ reset_custom_range_widgets (popover); priv->journal_timestamp_range = GL_SEARCH_POPOVER_JOURNAL_TIMESTAMP_RANGE_CURRENT_BOOT; g_object_notify_by_pspec (G_OBJECT (popover), obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE]); gtk_label_set_label (GTK_LABEL (priv->range_button_label), _("Current Boot")); } static void gl_search_popover_class_init (GlSearchPopoverClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); gobject_class->get_property = gl_search_popover_get_property; gobject_class->set_property = gl_search_popover_set_property; obj_properties[PROP_JOURNAL_SEARCH_FIELD] = g_param_spec_enum ("journal-search-field", "Journal Search Field", "The Journal search field by which to filter the logs", GL_TYPE_SEARCH_POPOVER_JOURNAL_FIELD_FILTER, GL_SEARCH_POPOVER_JOURNAL_FIELD_FILTER_ALL_AVAILABLE_FIELDS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_SEARCH_TYPE] = g_param_spec_enum ("search-type", "Search Type", "Do exact or substring search", GL_TYPE_QUERY_SEARCH_TYPE, GL_QUERY_SEARCH_TYPE_SUBSTRING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_JOURNAL_TIMESTAMP_RANGE] = g_param_spec_enum ("journal-timestamp-range", "Journal Timestamp Range", "The Timestamp range of the logs to be shown", GL_TYPE_SEARCH_POPOVER_JOURNAL_TIMESTAMP_RANGE, GL_SEARCH_POPOVER_JOURNAL_TIMESTAMP_RANGE_CURRENT_BOOT, 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-searchpopover.ui"); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, parameter_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, parameter_button_label); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, parameter_label_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, parameter_treeview); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, parameter_liststore); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, search_type_revealer); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, range_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, range_label_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, range_treeview); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, range_button_label); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, range_button_drop_down_image); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, range_liststore); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, menu_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_date_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_date_calendar_revealer); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_date_entry); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_date_button_label); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_time_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_time_hour_spin); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_time_minute_spin); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_time_second_spin); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_time_period_spin); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_time_button_label); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_date_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_date_calendar_revealer); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_date_entry); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_date_button_label); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_time_stack); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_time_hour_spin); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_time_minute_spin); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_time_second_spin); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_time_period_spin); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_time_button_label); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, start_time_period_label); gtk_widget_class_bind_template_child_private (widget_class, GlSearchPopover, end_time_period_label); gtk_widget_class_bind_template_callback (widget_class, search_popover_closed); gtk_widget_class_bind_template_callback (widget_class, select_parameter_button_clicked); gtk_widget_class_bind_template_callback (widget_class, on_parameter_treeview_row_activated); gtk_widget_class_bind_template_callback (widget_class, search_type_changed); gtk_widget_class_bind_template_callback (widget_class, select_range_button_clicked); gtk_widget_class_bind_template_callback (widget_class, on_range_treeview_row_activated); gtk_widget_class_bind_template_callback (widget_class, custom_range_submenu_back_button_clicked); gtk_widget_class_bind_template_callback (widget_class, end_time_spinbox_value_changed); gtk_widget_class_bind_template_callback (widget_class, end_time_button_clicked); gtk_widget_class_bind_template_callback (widget_class, end_date_entry_activate); gtk_widget_class_bind_template_callback (widget_class, end_date_calendar_day_selected); gtk_widget_class_bind_template_callback (widget_class, end_date_button_clicked); gtk_widget_class_bind_template_callback (widget_class, start_time_spinbox_value_changed); gtk_widget_class_bind_template_callback (widget_class, spinbox_entry_format_two_digits); gtk_widget_class_bind_template_callback (widget_class, spinbox_entry_validate_hour_min_sec); gtk_widget_class_bind_template_callback (widget_class, spinbox_format_time_period_to_int); gtk_widget_class_bind_template_callback (widget_class, spinbox_format_time_period_to_text); gtk_widget_class_bind_template_callback (widget_class, start_time_button_clicked); gtk_widget_class_bind_template_callback (widget_class, start_date_entry_activate); gtk_widget_class_bind_template_callback (widget_class, start_date_calendar_day_selected); gtk_widget_class_bind_template_callback (widget_class, start_date_button_clicked); } static void gl_search_popover_init (GlSearchPopover *popover) { GlSearchPopoverPrivate *priv; GSettings *settings; gtk_widget_init_template (GTK_WIDGET (popover)); priv = gl_search_popover_get_instance_private (popover); gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->parameter_treeview), (GtkTreeViewRowSeparatorFunc) parameter_treeview_row_seperator, popover, NULL); gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->range_treeview), (GtkTreeViewRowSeparatorFunc) range_treeview_row_seperator, popover, NULL); settings = g_settings_new (DESKTOP_SCHEMA); priv->clock_format = g_settings_get_enum (settings, CLOCK_FORMAT); /* Show only hour-minute-second spinboxes when time format is 24hr */ if (priv->clock_format == GL_UTIL_CLOCK_FORMAT_24HR) { GtkAdjustment *start_hour_adjustment; GtkAdjustment *end_hour_adjustment; /* Hide the AM/PM time period spinbuttons */ gtk_widget_hide (priv->start_time_period_spin); gtk_widget_hide (priv->start_time_period_label); gtk_widget_hide (priv->end_time_period_spin); gtk_widget_hide (priv->end_time_period_label); /* Set 0-23 as range for hour spinbutton */ start_hour_adjustment = gtk_adjustment_new (23.0, 0.0, 23.0, 5.0, 0.0, 0.0); end_hour_adjustment = gtk_adjustment_new (0.0, 0.0, 23.0, 5.0, 0.0, 0.0); gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (priv->start_time_hour_spin), start_hour_adjustment); gtk_spin_button_set_adjustment (GTK_SPIN_BUTTON (priv->end_time_hour_spin), end_hour_adjustment); } priv->custom_start_timestamp = 0; priv->custom_end_timestamp = 0; g_object_unref (settings); } GtkWidget * gl_search_popover_new (void) { return g_object_new (GL_TYPE_SEARCH_POPOVER, NULL); }