Files
sysprof/src/sp-window.c
Christian Hergert 36008c4ab4 window: notify the user when no valid samples were collected
If we failed to collect any samples in the target profile, let the user
know why the callgraph is empty.
2016-04-15 18:11:31 -07:00

955 lines
28 KiB
C

/* sp-window.c
*
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
*
* 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 <errno.h>
#include <glib/gi18n.h>
#include <sysprof.h>
#include <sysprof-ui.h>
#include "sp-application.h"
#include "sp-window.h"
#include "sp-window-settings.h"
struct _SpWindow
{
GtkApplicationWindow parent_instance;
SpWindowState state;
SpProfiler *profiler;
SpCaptureReader *reader;
/* Gtk widget template children */
SpCallgraphView *callgraph_view;
SpEmptyStateView *empty_view;
GtkMenuButton *gear_menu_button;
GtkInfoBar *info_bar;
GtkLabel *info_bar_label;
GtkRevealer *info_bar_revealer;
SpProfilerMenuButton *profiler_menu_button;
SpRecordingStateView *recording_view;
GtkButton *record_button;
GtkLabel *subtitle;
GtkLabel *stat_label;
GtkLabel *title;
GtkStack *view_stack;
guint stats_handler;
guint closing : 1;
};
G_DEFINE_TYPE (SpWindow, sp_window, GTK_TYPE_APPLICATION_WINDOW)
static void sp_window_set_state (SpWindow *self,
SpWindowState state);
enum {
START_RECORDING,
STOP_RECORDING,
N_SIGNALS
};
static guint signals [N_SIGNALS];
static void sp_window_set_profiler (SpWindow *self,
SpProfiler *profiler);
static void
sp_window_notify_user (SpWindow *self,
GtkMessageType message_type,
const gchar *format,
...)
{
g_autofree gchar *str = NULL;
va_list args;
g_assert (SP_IS_WINDOW (self));
g_assert (format != NULL);
va_start (args, format);
str = g_strdup_vprintf (format, args);
va_end (args);
gtk_info_bar_set_message_type (self->info_bar, message_type);
gtk_label_set_label (self->info_bar_label, str);
gtk_revealer_set_reveal_child (self->info_bar_revealer, TRUE);
}
static void
sp_window_action_set (SpWindow *self,
const gchar *action_name,
const gchar *first_property,
...)
{
gpointer action;
va_list args;
g_assert (SP_IS_WINDOW (self));
g_assert (action_name != NULL);
action = g_action_map_lookup_action (G_ACTION_MAP (self), action_name);
if (action == NULL)
{
g_warning ("Failed to locate action \"%s\"", action_name);
return;
}
va_start (args, first_property);
g_object_set_valist (action, first_property, args);
va_end (args);
}
static gboolean
sp_window_update_stats (gpointer data)
{
SpWindow *self = data;
g_assert (SP_IS_WINDOW (self));
if (self->profiler != NULL)
{
SpCaptureWriter *writer;
if (NULL != (writer = sp_profiler_get_writer (self->profiler)))
{
g_autofree gchar *str = NULL;
SpCaptureStat stbuf;
guint count;
sp_capture_writer_stat (writer, &stbuf);
count = stbuf.frame_count[SP_CAPTURE_FRAME_SAMPLE];
str = g_strdup_printf (_("Samples: %u"), count);
gtk_label_set_label (self->stat_label, str);
}
}
return G_SOURCE_CONTINUE;
}
static void
sp_window_update_subtitle (SpWindow *self)
{
g_autofree gchar *relative = NULL;
const gchar *filename;
const gchar *date;
GTimeVal tv;
g_assert (SP_IS_WINDOW (self));
g_assert (self->reader != NULL);
if (NULL != (filename = sp_capture_reader_get_filename (self->reader)))
{
g_autoptr(GFile) home = NULL;
g_autoptr(GFile) file = NULL;
file = g_file_new_for_path (filename);
home = g_file_new_for_path (g_get_home_dir ());
if (g_file_has_prefix (file, home))
filename = relative = g_file_get_relative_path (home, file);
}
if (filename == NULL)
filename = _("[Memory Capture]");
date = sp_capture_reader_get_time (self->reader);
if (g_time_val_from_iso8601 (date, &tv))
{
g_autoptr(GDateTime) dt = NULL;
g_autofree gchar *str = NULL;
g_autofree gchar *label = NULL;
dt = g_date_time_new_from_timeval_local (&tv);
str = g_date_time_format (dt, "%x %X");
label = g_strdup_printf (_("%s - %s"), filename, str);
gtk_label_set_label (self->subtitle, label);
}
else
gtk_label_set_label (self->subtitle, filename);
}
static void
sp_window_build_profile_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SpProfile *profile = (SpProfile *)object;
g_autoptr(SpWindow) self = user_data;
g_autoptr(GError) error = NULL;
g_assert (SP_IS_CALLGRAPH_PROFILE (profile));
g_assert (SP_IS_WINDOW (self));
g_assert (G_IS_ASYNC_RESULT (result));
if (!sp_profile_generate_finish (profile, result, &error))
{
sp_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", error->message);
sp_window_set_state (self, SP_WINDOW_STATE_EMPTY);
return;
}
sp_callgraph_view_set_profile (self->callgraph_view, SP_CALLGRAPH_PROFILE (profile));
sp_window_set_state (self, SP_WINDOW_STATE_BROWSING);
if (sp_callgraph_view_get_n_functions (self->callgraph_view) == 0)
sp_window_notify_user (self,
GTK_MESSAGE_WARNING,
_("Not enough samples were collected to generate a callgraph"));
}
static void
sp_window_build_profile (SpWindow *self)
{
g_autoptr(SpProfile) profile = NULL;
g_assert (SP_IS_WINDOW (self));
g_assert (self->reader != NULL);
profile = sp_callgraph_profile_new ();
sp_profile_set_reader (profile, self->reader);
sp_profile_generate (profile,
NULL,
sp_window_build_profile_cb,
g_object_ref (self));
}
static void
add_class (gpointer widget,
const gchar *name)
{
g_assert (GTK_IS_WIDGET (widget));
gtk_style_context_add_class (gtk_widget_get_style_context (widget), name);
}
static void
remove_class (gpointer widget,
const gchar *name)
{
g_assert (GTK_IS_WIDGET (widget));
gtk_style_context_remove_class (gtk_widget_get_style_context (widget), name);
}
static void
sp_window_set_state (SpWindow *self,
SpWindowState state)
{
g_autoptr(SpProfiler) profiler = NULL;
g_assert (SP_IS_WINDOW (self));
if (self->state == state)
return;
self->state = state;
switch (state)
{
case SP_WINDOW_STATE_EMPTY:
case SP_WINDOW_STATE_FAILED:
gtk_button_set_label (self->record_button, _("Record"));
gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE);
add_class (self->record_button, "suggsted-action");
remove_class (self->record_button, "destructive-action");
if (state == SP_WINDOW_STATE_FAILED)
gtk_stack_set_visible_child_name (self->view_stack, "failed");
else
gtk_stack_set_visible_child_name (self->view_stack, "empty");
gtk_label_set_label (self->subtitle, _("Not running"));
sp_callgraph_view_set_profile (self->callgraph_view, NULL);
gtk_widget_set_visible (GTK_WIDGET (self->stat_label), FALSE);
g_clear_pointer (&self->reader, sp_capture_reader_unref);
sp_window_action_set (self, "close-capture", "enabled", FALSE, NULL);
sp_window_action_set (self, "save-capture", "enabled", FALSE, NULL);
sp_window_action_set (self, "screenshot", "enabled", FALSE, NULL);
profiler = sp_local_profiler_new ();
sp_window_set_profiler (self, profiler);
break;
case SP_WINDOW_STATE_RECORDING:
gtk_button_set_label (self->record_button, _("Stop"));
gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE);
remove_class (self->record_button, "suggsted-action");
add_class (self->record_button, "destructive-action");
gtk_stack_set_visible_child_name (self->view_stack, "recording");
gtk_label_set_label (self->subtitle, _("Recording…"));
gtk_widget_set_visible (GTK_WIDGET (self->stat_label), TRUE);
g_clear_pointer (&self->reader, sp_capture_reader_unref);
sp_callgraph_view_set_profile (self->callgraph_view, NULL);
sp_window_action_set (self, "close-capture", "enabled", FALSE, NULL);
sp_window_action_set (self, "save-capture", "enabled", FALSE, NULL);
sp_window_action_set (self, "screenshot", "enabled", FALSE, NULL);
break;
case SP_WINDOW_STATE_PROCESSING:
gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), FALSE);
gtk_label_set_label (self->subtitle, _("Building profile…"));
sp_window_action_set (self, "close-capture", "enabled", FALSE, NULL);
sp_window_action_set (self, "save-capture", "enabled", FALSE, NULL);
sp_window_action_set (self, "screenshot", "enabled", FALSE, NULL);
sp_window_build_profile (self);
break;
case SP_WINDOW_STATE_BROWSING:
gtk_button_set_label (self->record_button, _("Record"));
gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE);
add_class (self->record_button, "suggsted-action");
remove_class (self->record_button, "destructive-action");
gtk_widget_set_visible (GTK_WIDGET (self->stat_label), TRUE);
gtk_stack_set_visible_child_name (self->view_stack, "browsing");
sp_window_update_stats (self);
sp_window_update_subtitle (self);
sp_window_action_set (self, "close-capture", "enabled", TRUE, NULL);
sp_window_action_set (self, "save-capture", "enabled", TRUE, NULL);
sp_window_action_set (self, "screenshot", "enabled", TRUE, NULL);
profiler = sp_local_profiler_new ();
sp_window_set_profiler (self, profiler);
break;
default:
g_warning ("Unknown state: %0d", state);
break;
}
}
static void
sp_window_enable_stats (SpWindow *self)
{
g_assert (SP_IS_WINDOW (self));
if (self->stats_handler == 0)
self->stats_handler =
g_timeout_add_seconds (1, sp_window_update_stats, self);
}
static void
sp_window_disable_stats (SpWindow *self)
{
g_assert (SP_IS_WINDOW (self));
if (self->stats_handler != 0)
{
g_source_remove (self->stats_handler);
self->stats_handler = 0;
}
}
static void
sp_window_add_sources (SpWindow *window,
SpProfiler *profiler)
{
g_autoptr(SpSource) proc_source = NULL;
g_autoptr(SpSource) perf_source = NULL;
g_assert (SP_IS_WINDOW (window));
g_assert (SP_IS_PROFILER (profiler));
proc_source = sp_proc_source_new ();
sp_profiler_add_source (profiler, proc_source);
perf_source = sp_perf_source_new ();
sp_profiler_add_source (profiler, perf_source);
}
static void
sp_window_start_recording (SpWindow *self)
{
g_assert (SP_IS_WINDOW (self));
if (self->state == SP_WINDOW_STATE_RECORDING)
{
/* SpProfiler::stopped will move us to generating */
gtk_label_set_label (self->subtitle, _("Stopping…"));
sp_profiler_stop (self->profiler);
return;
}
if ((self->state == SP_WINDOW_STATE_EMPTY) ||
(self->state == SP_WINDOW_STATE_FAILED) ||
(self->state == SP_WINDOW_STATE_BROWSING))
{
sp_window_add_sources (self, self->profiler);
sp_window_set_state (self, SP_WINDOW_STATE_RECORDING);
sp_window_enable_stats (self);
sp_profiler_start (self->profiler);
return;
}
}
static void
sp_window_stop_recording (SpWindow *self)
{
g_assert (SP_IS_WINDOW (self));
if (self->state == SP_WINDOW_STATE_RECORDING)
{
if (self->profiler != NULL)
sp_profiler_stop (self->profiler);
}
}
static void
sp_window_hide_info_bar_revealer (SpWindow *self)
{
g_assert (SP_IS_WINDOW (self));
gtk_revealer_set_reveal_child (self->info_bar_revealer, FALSE);
}
static void
sp_window_profiler_stopped (SpWindow *self,
SpProfiler *profiler)
{
g_autoptr(SpCaptureReader) reader = NULL;
g_autoptr(GError) error = NULL;
SpCaptureWriter *writer;
g_assert (SP_IS_WINDOW (self));
g_assert (SP_IS_PROFILER (profiler));
sp_window_disable_stats (self);
if (self->closing)
{
gtk_window_close (GTK_WINDOW (self));
return;
}
if (self->state == SP_WINDOW_STATE_FAILED)
return;
writer = sp_profiler_get_writer (profiler);
reader = sp_capture_writer_create_reader (writer, &error);
if (reader == NULL)
{
sp_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", error->message);
sp_window_set_state (self, SP_WINDOW_STATE_EMPTY);
return;
}
g_clear_pointer (&self->reader, sp_capture_reader_unref);
self->reader = g_steal_pointer (&reader);
sp_window_build_profile (self);
}
static void
sp_window_profiler_failed (SpWindow *self,
const GError *reason,
SpProfiler *profiler)
{
g_assert (SP_IS_WINDOW (self));
g_assert (reason != NULL);
g_assert (SP_IS_PROFILER (profiler));
sp_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", reason->message);
sp_window_set_state (self, SP_WINDOW_STATE_FAILED);
}
static void
sp_window_set_profiler (SpWindow *self,
SpProfiler *profiler)
{
g_assert (SP_IS_WINDOW (self));
g_assert (SP_IS_PROFILER (profiler));
if (self->profiler != profiler)
{
if (self->profiler != NULL)
{
if (sp_profiler_get_is_running (self->profiler))
sp_profiler_stop (self->profiler);
sp_profiler_menu_button_set_profiler (self->profiler_menu_button, NULL);
sp_recording_state_view_set_profiler (self->recording_view, NULL);
g_clear_object (&self->profiler);
}
if (profiler != NULL)
{
if (!sp_profiler_get_is_mutable (profiler))
{
g_warning ("Ignoring attempt to set profiler to an already running session!");
return;
}
self->profiler = g_object_ref (profiler);
g_signal_connect_object (profiler,
"stopped",
G_CALLBACK (sp_window_profiler_stopped),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (profiler,
"failed",
G_CALLBACK (sp_window_profiler_failed),
self,
G_CONNECT_SWAPPED);
sp_profiler_menu_button_set_profiler (self->profiler_menu_button, profiler);
sp_recording_state_view_set_profiler (self->recording_view, profiler);
}
}
}
static void
sp_window_open_capture (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
SpWindow *self = user_data;
g_assert (G_IS_SIMPLE_ACTION (action));
g_assert (variant == NULL);
g_assert (SP_IS_WINDOW (self));
sp_window_open_from_dialog (self);
}
static void
sp_window_save_capture (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
g_autoptr(SpCaptureReader) reader = NULL;
SpWindow *self = user_data;
GtkWidget *dialog;
GtkResponseType response;
g_assert (G_IS_SIMPLE_ACTION (action));
g_assert (variant == NULL);
g_assert (SP_IS_WINDOW (self));
if (self->reader == NULL)
return;
reader = sp_capture_reader_ref (self->reader);
dialog = gtk_file_chooser_dialog_new (_("Save Capture As"),
GTK_WINDOW (self),
GTK_FILE_CHOOSER_ACTION_SAVE,
_("Cancel"), GTK_RESPONSE_CANCEL,
_("Save"), GTK_RESPONSE_OK,
NULL);
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
response = gtk_dialog_run (GTK_DIALOG (dialog));
if (response == GTK_RESPONSE_OK)
{
g_autofree gchar *filename = NULL;
g_autoptr(GError) error = NULL;
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
if (filename == NULL)
goto failure;
if (!g_str_has_suffix (filename, ".syscap"))
{
gchar *tmp;
tmp = g_strdup_printf ("%s.syscap", filename);
g_free (filename);
filename = tmp;
}
/* this should really be done outside the main loop. */
if (!sp_capture_reader_save_as (reader, filename, &error))
{
sp_window_notify_user (self,
GTK_MESSAGE_ERROR,
_("An error occurred while attempting to save your capture: %s"),
error->message);
goto failure;
}
}
failure:
gtk_widget_destroy (dialog);
}
static void
sp_window_close_capture (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
SpWindow *self = user_data;
g_assert (G_IS_SIMPLE_ACTION (action));
g_assert (variant == NULL);
g_assert (SP_IS_WINDOW (self));
sp_window_set_state (self, SP_WINDOW_STATE_EMPTY);
}
static void
sp_window_record_button_clicked (SpWindow *self,
GtkButton *button)
{
g_assert (SP_IS_WINDOW (self));
g_assert (GTK_IS_BUTTON (button));
if (self->state == SP_WINDOW_STATE_RECORDING)
sp_window_stop_recording (self);
else
sp_window_start_recording (self);
}
static void
sp_window_screenshot (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
SpWindow *self = user_data;
g_autofree gchar *str = NULL;
GtkWindow *window;
GtkScrolledWindow *scroller;
GtkTextView *text_view;
g_assert (G_IS_SIMPLE_ACTION (action));
g_assert (SP_IS_WINDOW (self));
if (NULL == (str = sp_callgraph_view_screenshot (self->callgraph_view)))
return;
window = g_object_new (GTK_TYPE_WINDOW,
"title", "Sysprof",
"default-width", 800,
"default-height", 600,
"transient-for", self,
NULL);
scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
"visible", TRUE,
NULL);
gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (scroller));
text_view = g_object_new (GTK_TYPE_TEXT_VIEW,
"editable", FALSE,
"monospace", TRUE,
"visible", TRUE,
NULL);
gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (text_view));
gtk_text_buffer_set_text (gtk_text_view_get_buffer (text_view), str, -1);
gtk_window_present (window);
}
static gboolean
sp_window_delete_event (GtkWidget *widget,
GdkEventAny *event)
{
SpWindow *self = (SpWindow *)widget;
g_assert (SP_IS_WINDOW (self));
g_assert (event != NULL);
if (self->state == SP_WINDOW_STATE_RECORDING)
{
if (self->profiler != NULL)
{
if (self->closing == FALSE)
{
self->closing = TRUE;
sp_profiler_stop (self->profiler);
return GDK_EVENT_STOP;
}
}
}
return GDK_EVENT_PROPAGATE;
}
static void
sp_window_destroy (GtkWidget *widget)
{
SpWindow *self = (SpWindow *)widget;
g_clear_object (&self->profiler);
g_clear_pointer (&self->reader, sp_capture_reader_unref);
sp_window_disable_stats (self);
GTK_WIDGET_CLASS (sp_window_parent_class)->destroy (widget);
}
static void
sp_window_constructed (GObject *object)
{
SpWindow *self = (SpWindow *)object;
g_autoptr(SpProfiler) profiler = NULL;
G_OBJECT_CLASS (sp_window_parent_class)->constructed (object);
profiler = sp_local_profiler_new ();
sp_window_set_profiler (self, profiler);
sp_window_set_state (self, SP_WINDOW_STATE_EMPTY);
}
static void
sp_window_class_init (SpWindowClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkBindingSet *binding_set;
object_class->constructed = sp_window_constructed;
widget_class->delete_event = sp_window_delete_event;
widget_class->destroy = sp_window_destroy;
signals [START_RECORDING] =
g_signal_new_class_handler ("start-recording",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (sp_window_start_recording),
NULL, NULL, NULL, G_TYPE_NONE, 0);
signals [STOP_RECORDING] =
g_signal_new_class_handler ("stop-recording",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (sp_window_stop_recording),
NULL, NULL, NULL, G_TYPE_NONE, 0);
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "stop-recording", 0);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sp-window.ui");
gtk_widget_class_bind_template_child (widget_class, SpWindow, callgraph_view);
gtk_widget_class_bind_template_child (widget_class, SpWindow, empty_view);
gtk_widget_class_bind_template_child (widget_class, SpWindow, gear_menu_button);
gtk_widget_class_bind_template_child (widget_class, SpWindow, info_bar);
gtk_widget_class_bind_template_child (widget_class, SpWindow, info_bar_label);
gtk_widget_class_bind_template_child (widget_class, SpWindow, info_bar_revealer);
gtk_widget_class_bind_template_child (widget_class, SpWindow, profiler_menu_button);
gtk_widget_class_bind_template_child (widget_class, SpWindow, record_button);
gtk_widget_class_bind_template_child (widget_class, SpWindow, recording_view);
gtk_widget_class_bind_template_child (widget_class, SpWindow, stat_label);
gtk_widget_class_bind_template_child (widget_class, SpWindow, subtitle);
gtk_widget_class_bind_template_child (widget_class, SpWindow, title);
gtk_widget_class_bind_template_child (widget_class, SpWindow, view_stack);
}
static void
sp_window_init (SpWindow *self)
{
GAction *action;
static GActionEntry action_entries[] = {
{ "close-capture", sp_window_close_capture },
{ "open-capture", sp_window_open_capture },
{ "save-capture", sp_window_save_capture },
{ "screenshot", sp_window_screenshot },
};
GtkApplication *app;
GMenu *menu;
gtk_widget_init_template (GTK_WIDGET (self));
/*
* Hookup widget signals.
*/
g_signal_connect_object (self->info_bar,
"response",
G_CALLBACK (sp_window_hide_info_bar_revealer),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->info_bar,
"close",
G_CALLBACK (sp_window_hide_info_bar_revealer),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->record_button,
"clicked",
G_CALLBACK (sp_window_record_button_clicked),
self,
G_CONNECT_SWAPPED);
/*
* Setup actions for the window.
*/
g_action_map_add_action_entries (G_ACTION_MAP (self),
action_entries,
G_N_ELEMENTS (action_entries),
self);
/*
* Setup our gear (hamburger) menu.
*/
app = GTK_APPLICATION (g_application_get_default ());
menu = gtk_application_get_menu_by_id (app, "gear-menu");
gtk_menu_button_set_menu_model (self->gear_menu_button, G_MENU_MODEL (menu));
/*
* Restore previous window settings.
*/
sp_window_settings_register (GTK_WINDOW (self));
/*
* Set default focus to the record button for quick workflow of
* launch, enter, escape, view.
*/
gtk_window_set_focus (GTK_WINDOW (self), GTK_WIDGET (self->record_button));
}
static void
sp_window_open_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SpWindow *self = (SpWindow *)object;
g_autoptr(SpCaptureReader) reader = NULL;
g_autoptr(GError) error = NULL;
g_assert (SP_IS_WINDOW (self));
g_assert (G_IS_TASK (result));
reader = g_task_propagate_pointer (G_TASK (result), &error);
if (reader == NULL)
{
sp_window_notify_user (self,
GTK_MESSAGE_ERROR,
"%s", error->message);
return;
}
g_clear_pointer (&self->reader, sp_capture_reader_unref);
self->reader = g_steal_pointer (&reader);
sp_window_set_state (self, SP_WINDOW_STATE_PROCESSING);
}
static void
sp_window_open_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autofree gchar *path = NULL;
SpCaptureReader *reader;
GFile *file = task_data;
GError *error = NULL;
g_assert (G_IS_TASK (task));
g_assert (SP_IS_WINDOW (source_object));
g_assert (G_IS_FILE (file));
path = g_file_get_path (file);
if (NULL == (reader = sp_capture_reader_new (path, &error)))
{
g_task_return_error (task, error);
return;
}
g_task_return_pointer (task, reader, (GDestroyNotify)sp_capture_reader_unref);
}
void
sp_window_open (SpWindow *self,
GFile *file)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SP_IS_WINDOW (self));
g_return_if_fail (G_IS_FILE (file));
if (!g_file_is_native (file))
{
sp_window_notify_user (self,
GTK_MESSAGE_ERROR,
_("The file \"%s\" could not be opened. Only local files are supported."),
g_file_get_uri (file));
return;
}
task = g_task_new (self, NULL, sp_window_open_cb, NULL);
g_task_set_task_data (task, g_object_ref (file), g_object_unref);
g_task_run_in_thread (task, sp_window_open_worker);
}
SpWindowState
sp_window_get_state (SpWindow *self)
{
g_return_val_if_fail (SP_IS_WINDOW (self), SP_WINDOW_STATE_0);
return self->state;
}
void
sp_window_open_from_dialog (SpWindow *self)
{
GtkFileFilter *filter;
GtkDialog *dialog;
g_assert (SP_IS_WINDOW (self));
dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
"action", GTK_FILE_CHOOSER_ACTION_OPEN,
"title", _("Open Capture"),
"transient-for", self,
NULL);
gtk_dialog_add_buttons (dialog,
_("Cancel"), GTK_RESPONSE_CANCEL,
_("Open"), GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Sysprof Captures"));
gtk_file_filter_add_pattern (filter, "*.syscap");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("All Files"));
gtk_file_filter_add_pattern (filter, "*");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
if (gtk_dialog_run (dialog) == GTK_RESPONSE_OK)
{
g_autoptr(GFile) file = NULL;
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
sp_window_open (self, file);
}
gtk_widget_destroy (GTK_WIDGET (dialog));
}