diff --git a/src/libsysprof-ui/sysprof-display.c b/src/libsysprof-ui/sysprof-display.c
index ca4760e8..dc904095 100644
--- a/src/libsysprof-ui/sysprof-display.c
+++ b/src/libsysprof-ui/sysprof-display.c
@@ -473,3 +473,66 @@ sysprof_display_open (SysprofDisplay *self,
update_title_child_property (self);
}
+
+void
+sysprof_display_save (SysprofDisplay *self)
+{
+ SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+ g_autoptr(GFile) file = NULL;
+ GtkFileChooserNative *native;
+ SysprofCaptureReader *reader;
+ GtkWindow *parent;
+ gint res;
+
+ g_return_if_fail (SYSPROF_IS_DISPLAY (self));
+
+ if (!(reader = sysprof_capture_view_get_reader (priv->capture_view)))
+ return;
+
+ parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)));
+
+ native = gtk_file_chooser_native_new (_("Save Recording"),
+ parent,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ _("Save"),
+ _("Cancel"));
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (native), TRUE);
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (native), TRUE);
+ gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER (native), TRUE);
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (native), "capture.sysprof");
+
+ res = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native));
+
+ switch (res)
+ {
+ case GTK_RESPONSE_ACCEPT:
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native));
+
+ if (g_file_is_native (file))
+ {
+ g_autofree gchar *path = g_file_get_path (file);
+ g_autoptr(GError) error = NULL;
+
+ if (!sysprof_capture_reader_save_as (reader, path, &error))
+ {
+ GtkWidget *msg;
+
+ msg = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Failed to save recording: %s"),
+ error->message);
+ gtk_window_present (GTK_WINDOW (msg));
+ g_signal_connect (msg, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (native));
+}
diff --git a/src/libsysprof-ui/sysprof-display.h b/src/libsysprof-ui/sysprof-display.h
index 5826ab86..6bdd43ba 100644
--- a/src/libsysprof-ui/sysprof-display.h
+++ b/src/libsysprof-ui/sysprof-display.h
@@ -51,5 +51,7 @@ gboolean sysprof_display_is_empty (SysprofDisplay *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_display_open (SysprofDisplay *self,
GFile *file);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_display_save (SysprofDisplay *self);
G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-notebook.c b/src/libsysprof-ui/sysprof-notebook.c
index bf89928f..30a0aac6 100644
--- a/src/libsysprof-ui/sysprof-notebook.c
+++ b/src/libsysprof-ui/sysprof-notebook.c
@@ -160,3 +160,27 @@ sysprof_notebook_open (SysprofNotebook *self,
sysprof_display_open (SYSPROF_DISPLAY (display), file);
}
+
+static SysprofDisplay *
+sysprof_notebook_get_current (SysprofNotebook *self)
+{
+ gint page;
+
+ g_assert (SYSPROF_IS_NOTEBOOK (self));
+
+ if ((page = gtk_notebook_get_current_page (GTK_NOTEBOOK (self))) >= 0)
+ return SYSPROF_DISPLAY (gtk_notebook_get_nth_page (GTK_NOTEBOOK (self), page));
+
+ return NULL;
+}
+
+void
+sysprof_notebook_save (SysprofNotebook *self)
+{
+ SysprofDisplay *display;
+
+ g_return_if_fail (SYSPROF_IS_NOTEBOOK (self));
+
+ if ((display = sysprof_notebook_get_current (self)))
+ sysprof_display_save (display);
+}
diff --git a/src/libsysprof-ui/sysprof-notebook.h b/src/libsysprof-ui/sysprof-notebook.h
index de3cfc18..c3f3d3df 100644
--- a/src/libsysprof-ui/sysprof-notebook.h
+++ b/src/libsysprof-ui/sysprof-notebook.h
@@ -46,5 +46,7 @@ void sysprof_notebook_close_current (SysprofNotebook *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_notebook_open (SysprofNotebook *self,
GFile *file);
+SYSPROF_AVAILABLE_IN_ALL
+void sysprof_notebook_save (SysprofNotebook *self);
G_END_DECLS
diff --git a/src/sysprof/gtk/menus.ui b/src/sysprof/gtk/menus.ui
index 3f9aa3d6..88e380a6 100644
--- a/src/sysprof/gtk/menus.ui
+++ b/src/sysprof/gtk/menus.ui
@@ -20,6 +20,13 @@
<primary>o
+