[sysprof] libsysprof-ui: start on new tabbed design



commit 691a1bc2b05817cf2e3cbd8a8a378cbc32ae7609
Author: Christian Hergert <chergert redhat com>
Date:   Wed May 15 23:56:30 2019 -0700

    libsysprof-ui: start on new tabbed design

 src/libsysprof-ui/libsysprof-ui.gresource.xml |    2 +
 src/libsysprof-ui/meson.build                 |    5 +
 src/libsysprof-ui/sysprof-capture-view.c      |   21 +-
 src/libsysprof-ui/sysprof-display.c           |  159 ++++
 src/libsysprof-ui/sysprof-display.h           |   45 ++
 src/libsysprof-ui/sysprof-notebook.c          |  167 ++++
 src/libsysprof-ui/sysprof-notebook.h          |   47 ++
 src/libsysprof-ui/sysprof-tab.c               |  152 ++++
 src/libsysprof-ui/sysprof-tab.h               |   35 +
 src/libsysprof-ui/sysprof-ui.h                |    2 +
 src/libsysprof-ui/ui/sysprof-display.ui       |   37 +
 src/libsysprof-ui/ui/sysprof-tab.ui           |   45 ++
 src/sysprof/sysprof-application.c             |   66 +-
 src/sysprof/sysprof-window.c                  | 1017 ++-----------------------
 src/sysprof/sysprof-window.h                  |   35 +-
 src/sysprof/ui/sysprof-window.ui              |  283 +------
 16 files changed, 813 insertions(+), 1305 deletions(-)
---
diff --git a/src/libsysprof-ui/libsysprof-ui.gresource.xml b/src/libsysprof-ui/libsysprof-ui.gresource.xml
index 46ac5f8..849b877 100644
--- a/src/libsysprof-ui/libsysprof-ui.gresource.xml
+++ b/src/libsysprof-ui/libsysprof-ui.gresource.xml
@@ -11,6 +11,7 @@
 
     <file preprocess="xml-stripblanks">ui/sysprof-callgraph-view.ui</file>
     <file preprocess="xml-stripblanks">ui/sysprof-capture-view.ui</file>
+    <file preprocess="xml-stripblanks">ui/sysprof-display.ui</file>
     <file preprocess="xml-stripblanks">ui/sysprof-details-view.ui</file>
     <file preprocess="xml-stripblanks">ui/sysprof-empty-state-view.ui</file>
     <file preprocess="xml-stripblanks">ui/sysprof-failed-state-view.ui</file>
@@ -18,6 +19,7 @@
     <file preprocess="xml-stripblanks">ui/sysprof-process-model-row.ui</file>
     <file preprocess="xml-stripblanks">ui/sysprof-profiler-menu-button.ui</file>
     <file preprocess="xml-stripblanks">ui/sysprof-recording-state-view.ui</file>
+    <file preprocess="xml-stripblanks">ui/sysprof-tab.ui</file>
     <file preprocess="xml-stripblanks">ui/sysprof-visualizer-view.ui</file>
   </gresource>
 </gresources>
diff --git a/src/libsysprof-ui/meson.build b/src/libsysprof-ui/meson.build
index e7c4645..29972b7 100644
--- a/src/libsysprof-ui/meson.build
+++ b/src/libsysprof-ui/meson.build
@@ -3,6 +3,7 @@ libsysprof_ui_public_sources = [
   'sysprof-callgraph-view.c',
   'sysprof-color-cycle.c',
   'sysprof-cpu-visualizer-row.c',
+  'sysprof-display.c',
   'sysprof-empty-state-view.c',
   'sysprof-failed-state-view.c',
   'sysprof-line-visualizer-row.c',
@@ -11,6 +12,7 @@ libsysprof_ui_public_sources = [
   'sysprof-mark-visualizer-row.c',
   'sysprof-model-filter.c',
   'sysprof-multi-paned.c',
+  'sysprof-notebook.c',
   'sysprof-process-model-row.c',
   'sysprof-profiler-menu-button.c',
   'sysprof-recording-state-view.c',
@@ -27,6 +29,7 @@ libsysprof_ui_private_sources = [
   'sysprof-details-view.c',
   'sysprof-cell-renderer-duration.c',
   'sysprof-cell-renderer-percent.c',
+  'sysprof-tab.c',
   'sysprof-theme-manager.c',
   '../stackstash.c',
 ]
@@ -36,6 +39,7 @@ libsysprof_ui_public_headers = [
   'sysprof-callgraph-view.h',
   'sysprof-cell-renderer-percent.h',
   'sysprof-cpu-visualizer-row.h',
+  'sysprof-display.h',
   'sysprof-empty-state-view.h',
   'sysprof-failed-state-view.h',
   'sysprof-line-visualizer-row.h',
@@ -44,6 +48,7 @@ libsysprof_ui_public_headers = [
   'sysprof-mark-visualizer-row.h',
   'sysprof-model-filter.h',
   'sysprof-multi-paned.h',
+  'sysprof-notebook.h',
   'sysprof-process-model-row.h',
   'sysprof-profiler-menu-button.h',
   'sysprof-recording-state-view.h',
diff --git a/src/libsysprof-ui/sysprof-capture-view.c b/src/libsysprof-ui/sysprof-capture-view.c
index 39a447a..0322fd8 100644
--- a/src/libsysprof-ui/sysprof-capture-view.c
+++ b/src/libsysprof-ui/sysprof-capture-view.c
@@ -226,7 +226,7 @@ sysprof_capture_view_scan_worker (GTask        *task,
   SysprofCaptureReader *reader = task_data;
   SysprofCaptureFeatures features = {0};
   SysprofCaptureFrame frame;
-  SysprofCaptureStat st_buf = {0};
+  SysprofCaptureStat st_buf = {{0}};
 
   g_assert (SYSPROF_IS_CAPTURE_VIEW (self));
   g_assert (G_IS_TASK (task));
@@ -775,3 +775,22 @@ sysprof_capture_view_fit_to_width (SysprofCaptureView *self)
   zoom = sysprof_zoom_manager_fit_zoom_for_duration (priv->zoom_manager, duration, width);
   sysprof_zoom_manager_set_zoom (priv->zoom_manager, zoom);
 }
+
+/**
+ * sysprof_capture_view_get_reader:
+ *
+ * Gets the reader for the view, if any.
+ *
+ * Returns: (transfer none): a #SysprofCaptureReader or %NULL
+ *
+ * Since: 3.34
+ */
+SysprofCaptureReader *
+sysprof_capture_view_get_reader (SysprofCaptureView *self)
+{
+  SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self);
+
+  g_return_val_if_fail (SYSPROF_IS_CAPTURE_VIEW (self), NULL);
+
+  return priv->reader;
+}
diff --git a/src/libsysprof-ui/sysprof-display.c b/src/libsysprof-ui/sysprof-display.c
new file mode 100644
index 0000000..1a90c86
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-display.c
@@ -0,0 +1,159 @@
+/* sysprof-display.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "sysprof-display"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <sysprof-capture.h>
+#include <sysprof-ui.h>
+#include <sysprof.h>
+
+#include "sysprof-capture-view.h"
+#include "sysprof-display.h"
+#include "sysprof-empty-state-view.h"
+#include "sysprof-recording-state-view.h"
+
+typedef struct
+{
+  SysprofCaptureView        *capture_view;
+  SysprofEmptyStateView     *empty_view;
+  SysprofRecordingStateView *recording_view;
+} SysprofDisplayPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofDisplay, sysprof_display, GTK_TYPE_BIN)
+
+enum {
+  PROP_0,
+  PROP_TITLE,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/**
+ * sysprof_display_new:
+ *
+ * Create a new #SysprofDisplay.
+ *
+ * Returns: (transfer full): a newly created #SysprofDisplay
+ *
+ * Since: 3.34
+ */
+GtkWidget *
+sysprof_display_new (void)
+{
+  return g_object_new (SYSPROF_TYPE_DISPLAY, NULL);
+}
+
+static gchar *
+sysprof_display_dup_title (SysprofDisplay *self)
+{
+  SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self);
+  SysprofCaptureReader *reader;
+
+  g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL);
+
+  if ((reader = sysprof_capture_view_get_reader (priv->capture_view)))
+    {
+      const gchar *filename;
+
+      if ((filename = sysprof_capture_reader_get_filename (reader)))
+        return g_strdup (filename);
+
+    }
+
+  return g_strdup (_("Unsaved Session"));
+}
+
+static void
+sysprof_display_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (sysprof_display_parent_class)->finalize (object);
+}
+
+static void
+sysprof_display_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  SysprofDisplay *self = (SysprofDisplay *)object;
+
+  switch (prop_id)
+    {
+    case PROP_TITLE:
+      g_value_take_string (value, sysprof_display_dup_title (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sysprof_display_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sysprof_display_class_init (SysprofDisplayClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = sysprof_display_finalize;
+  object_class->get_property = sysprof_display_get_property;
+  object_class->set_property = sysprof_display_set_property;
+
+  properties [PROP_TITLE] =
+    g_param_spec_string ("title",
+                         "Title",
+                         "The title of the display",
+                         NULL,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-display.ui");
+  gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay , empty_view);
+  gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay , recording_view);
+  gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay , capture_view);
+
+  g_type_ensure (SYSPROF_TYPE_CAPTURE_VIEW);
+  g_type_ensure (SYSPROF_TYPE_EMPTY_STATE_VIEW);
+  g_type_ensure (SYSPROF_TYPE_RECORDING_STATE_VIEW);
+}
+
+static void
+sysprof_display_init (SysprofDisplay *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/src/libsysprof-ui/sysprof-display.h b/src/libsysprof-ui/sysprof-display.h
new file mode 100644
index 0000000..0b6c735
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-display.h
@@ -0,0 +1,45 @@
+/* sysprof-display.h
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "sysprof-version-macros.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_DISPLAY (sysprof_display_get_type())
+
+SYSPROF_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (SysprofDisplay, sysprof_display, SYSPROF, DISPLAY, GtkBin)
+
+struct _SysprofDisplayClass
+{
+  GtkBinClass parent_class;
+
+  /*< private >*/
+  gpointer _reserved[16];
+} __attribute__((aligned(8)));
+
+SYSPROF_AVAILABLE_IN_ALL
+GtkWidget *sysprof_display_new (void);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-notebook.c b/src/libsysprof-ui/sysprof-notebook.c
new file mode 100644
index 0000000..0be4977
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-notebook.c
@@ -0,0 +1,167 @@
+/* sysprof-notebook.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "sysprof-notebook"
+
+#include "config.h"
+
+#include "sysprof-display.h"
+#include "sysprof-notebook.h"
+#include "sysprof-tab.h"
+
+typedef struct
+{
+  void *dummy;
+} SysprofNotebookPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SysprofNotebook, sysprof_notebook, GTK_TYPE_NOTEBOOK)
+
+enum {
+  PROP_0,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/**
+ * sysprof_notebook_new:
+ *
+ * Create a new #SysprofNotebook.
+ *
+ * Returns: (transfer full): a newly created #SysprofNotebook
+ *
+ * Since: 3.34
+ */
+GtkWidget *
+sysprof_notebook_new (void)
+{
+  return g_object_new (SYSPROF_TYPE_NOTEBOOK, NULL);
+}
+
+static void
+sysprof_notebook_page_added (GtkNotebook *notebook,
+                             GtkWidget   *child,
+                             guint        page_num)
+{
+  g_assert (SYSPROF_IS_NOTEBOOK (notebook));
+  g_assert (GTK_IS_WIDGET (child));
+
+  if (SYSPROF_IS_DISPLAY (child))
+    {
+      GtkWidget *tab = sysprof_tab_new (SYSPROF_DISPLAY (child));
+
+      gtk_notebook_set_tab_label (notebook, child, tab);
+      gtk_notebook_set_tab_reorderable (notebook, child, TRUE);
+    }
+
+  gtk_notebook_set_show_tabs (notebook,
+                              gtk_notebook_get_n_pages (notebook) > 1);
+}
+
+static void
+sysprof_notebook_page_removed (GtkNotebook *notebook,
+                               GtkWidget   *child,
+                               guint        page_num)
+{
+  g_assert (SYSPROF_IS_NOTEBOOK (notebook));
+  g_assert (GTK_IS_WIDGET (child));
+
+  if (gtk_notebook_get_n_pages (notebook) == 0)
+    {
+      child = sysprof_display_new ();
+      gtk_container_add (GTK_CONTAINER (notebook), child);
+      gtk_widget_show (child);
+    }
+
+  gtk_notebook_set_show_tabs (notebook,
+                              gtk_notebook_get_n_pages (notebook) > 1);
+}
+
+static void
+sysprof_notebook_finalize (GObject *object)
+{
+  SysprofNotebook *self = (SysprofNotebook *)object;
+  SysprofNotebookPrivate *priv = sysprof_notebook_get_instance_private (self);
+
+  G_OBJECT_CLASS (sysprof_notebook_parent_class)->finalize (object);
+}
+
+static void
+sysprof_notebook_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  SysprofNotebook *self = SYSPROF_NOTEBOOK (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sysprof_notebook_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  SysprofNotebook *self = SYSPROF_NOTEBOOK (object);
+
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sysprof_notebook_class_init (SysprofNotebookClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
+
+  object_class->finalize = sysprof_notebook_finalize;
+  object_class->get_property = sysprof_notebook_get_property;
+  object_class->set_property = sysprof_notebook_set_property;
+
+  notebook_class->page_added = sysprof_notebook_page_added;
+  notebook_class->page_removed = sysprof_notebook_page_removed;
+}
+
+static void
+sysprof_notebook_init (SysprofNotebook *self)
+{
+  gtk_notebook_set_show_border (GTK_NOTEBOOK (self), FALSE);
+  gtk_notebook_set_scrollable (GTK_NOTEBOOK (self), TRUE);
+  gtk_notebook_popup_enable (GTK_NOTEBOOK (self));
+}
+
+void
+sysprof_notebook_close_current (SysprofNotebook *self)
+{
+  gint page;
+
+  g_return_if_fail (SYSPROF_IS_NOTEBOOK (self));
+
+  if ((page = gtk_notebook_get_current_page (GTK_NOTEBOOK (self))) >= 0)
+    gtk_widget_destroy (gtk_notebook_get_nth_page (GTK_NOTEBOOK (self), page));
+}
diff --git a/src/libsysprof-ui/sysprof-notebook.h b/src/libsysprof-ui/sysprof-notebook.h
new file mode 100644
index 0000000..7a06439
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-notebook.h
@@ -0,0 +1,47 @@
+/* sysprof-notebook.h
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "sysprof-version-macros.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_NOTEBOOK (sysprof_notebook_get_type())
+
+SYSPROF_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (SysprofNotebook, sysprof_notebook, SYSPROF, NOTEBOOK, GtkNotebook)
+
+struct _SysprofNotebookClass
+{
+  GtkNotebookClass parent_class;
+
+  /*< private >*/
+  gpointer _reserved[16];
+};
+
+SYSPROF_AVAILABLE_IN_ALL
+GtkWidget *sysprof_notebook_new           (void);
+SYSPROF_AVAILABLE_IN_ALL
+void       sysprof_notebook_close_current (SysprofNotebook *self);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-tab.c b/src/libsysprof-ui/sysprof-tab.c
new file mode 100644
index 0000000..45c9254
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-tab.c
@@ -0,0 +1,152 @@
+/* sysprof-tab.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "sysprof-tab"
+
+#include "config.h"
+
+#include "sysprof-display.h"
+#include "sysprof-tab.h"
+
+struct _SysprofTab
+{
+  GtkBox          parent_instance;
+
+  GtkButton      *close_button;
+  GtkLabel       *title;
+
+  SysprofDisplay *display;
+};
+
+G_DEFINE_TYPE (SysprofTab, sysprof_tab, GTK_TYPE_BOX)
+
+enum {
+  PROP_0,
+  PROP_DISPLAY,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+GtkWidget *
+sysprof_tab_new (SysprofDisplay *display)
+{
+  return g_object_new (SYSPROF_TYPE_TAB,
+                       "display", display,
+                       NULL);
+}
+
+static void
+sysprof_tab_close_clicked (SysprofTab *self,
+                           GtkButton  *button)
+{
+  g_assert (SYSPROF_IS_TAB (self));
+  g_assert (GTK_IS_BUTTON (button));
+
+  if (self->display != NULL)
+    gtk_widget_destroy (GTK_WIDGET (self->display));
+}
+
+static void
+sysprof_tab_finalize (GObject *object)
+{
+  SysprofTab *self = (SysprofTab *)object;
+
+  g_clear_weak_pointer (&self->display);
+
+  G_OBJECT_CLASS (sysprof_tab_parent_class)->finalize (object);
+}
+
+static void
+sysprof_tab_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  SysprofTab *self = SYSPROF_TAB (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY:
+      g_value_set_object (value, self->display);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sysprof_tab_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  SysprofTab *self = SYSPROF_TAB (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY:
+      g_set_weak_pointer (&self->display, g_value_get_object (value));
+      g_object_bind_property (self->display, "title",
+                              self->title, "label",
+                              G_BINDING_SYNC_CREATE);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sysprof_tab_class_init (SysprofTabClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = sysprof_tab_finalize;
+  object_class->get_property = sysprof_tab_get_property;
+  object_class->set_property = sysprof_tab_set_property;
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-tab.ui");
+  gtk_widget_class_bind_template_child (widget_class, SysprofTab, close_button);
+  gtk_widget_class_bind_template_child (widget_class, SysprofTab, title);
+
+  properties [PROP_DISPLAY] =
+    g_param_spec_object ("display",
+                         "Display",
+                         "The display widget for the tab",
+                         SYSPROF_TYPE_DISPLAY,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+sysprof_tab_init (SysprofTab *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect_object (self->close_button,
+                           "clicked",
+                           G_CALLBACK (sysprof_tab_close_clicked),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
diff --git a/src/libsysprof-ui/sysprof-tab.h b/src/libsysprof-ui/sysprof-tab.h
new file mode 100644
index 0000000..1762244
--- /dev/null
+++ b/src/libsysprof-ui/sysprof-tab.h
@@ -0,0 +1,35 @@
+/* sysprof-tab.h
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "sysprof-display.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_TAB (sysprof_tab_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofTab, sysprof_tab, SYSPROF, TAB, GtkBox)
+
+GtkWidget *sysprof_tab_new (SysprofDisplay *display);
+
+G_END_DECLS
diff --git a/src/libsysprof-ui/sysprof-ui.h b/src/libsysprof-ui/sysprof-ui.h
index 52a3af8..51cbb5b 100644
--- a/src/libsysprof-ui/sysprof-ui.h
+++ b/src/libsysprof-ui/sysprof-ui.h
@@ -30,6 +30,7 @@ G_BEGIN_DECLS
 # include "sysprof-capture-view.h"
 # include "sysprof-cell-renderer-percent.h"
 # include "sysprof-cpu-visualizer-row.h"
+# include "sysprof-display.h"
 # include "sysprof-empty-state-view.h"
 # include "sysprof-failed-state-view.h"
 # include "sysprof-line-visualizer-row.h"
@@ -38,6 +39,7 @@ G_BEGIN_DECLS
 # include "sysprof-mark-visualizer-row.h"
 # include "sysprof-model-filter.h"
 # include "sysprof-multi-paned.h"
+# include "sysprof-notebook.h"
 # include "sysprof-process-model-row.h"
 # include "sysprof-profiler-menu-button.h"
 # include "sysprof-recording-state-view.h"
diff --git a/src/libsysprof-ui/ui/sysprof-display.ui b/src/libsysprof-ui/ui/sysprof-display.ui
new file mode 100644
index 0000000..1392b5a
--- /dev/null
+++ b/src/libsysprof-ui/ui/sysprof-display.ui
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="SysprofDisplay" parent="GtkBin">
+    <child>
+      <object class="GtkStack" id="stack">
+        <property name="homogeneous">false</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="SysprofEmptyStateView" id="empty_view">
+            <property name="title" translatable="yes">Welcome to Sysprof</property>
+            <property name="subtitle" translatable="yes">Start profiling your system with the 
&lt;b&gt;Record&lt;/b&gt; button above.</property>
+            <property name="visible">true</property>
+          </object>
+          <packing>
+            <property name="name">empty</property>
+          </packing>
+        </child>
+        <child>
+          <object class="SysprofCaptureView" id="capture_view">
+            <property name="visible">true</property>
+          </object>
+          <packing>
+            <property name="name">capture</property>
+          </packing>
+        </child>
+        <child>
+          <object class="SysprofRecordingStateView" id="recording_view">
+            <property name="visible">true</property>
+          </object>
+          <packing>
+            <property name="name">recording</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/libsysprof-ui/ui/sysprof-tab.ui b/src/libsysprof-ui/ui/sysprof-tab.ui
new file mode 100644
index 0000000..7f63472
--- /dev/null
+++ b/src/libsysprof-ui/ui/sysprof-tab.ui
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.0 -->
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <template class="SysprofTab" parent="GtkBox">
+    <property name="can_focus">False</property>
+    <property name="hexpand">False</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkLabel" id="title">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton" id="close_button">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="receives_default">False</property>
+        <child>
+          <object class="GtkImage">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="icon_name">window-close-symbolic</property>
+          </object>
+        </child>
+        <style>
+          <class name="flat"/>
+          <class name="small-button"/>
+        </style>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/src/sysprof/sysprof-application.c b/src/sysprof/sysprof-application.c
index 4405a6b..24e6cd7 100644
--- a/src/sysprof/sysprof-application.c
+++ b/src/sysprof/sysprof-application.c
@@ -36,9 +36,20 @@ struct {
   const gchar *action_name;
   const gchar *accels[12];
 } default_accels[] = {
-  { "zoom.zoom-in", { "<Primary>plus", "<Primary>KP_Add", "<Primary>equal", "ZoomIn", NULL } },
-  { "zoom.zoom-out", { "<Primary>minus", "<Primary>KP_Subtract", "ZoomOut", NULL } },
-  { "zoom.zoom-one", { "<Primary>0", "<Primary>KP_0", NULL } },
+  { "zoom.zoom-in",       { "<Primary>plus", "<Primary>KP_Add", "<Primary>equal", "ZoomIn", NULL } },
+  { "zoom.zoom-out",      { "<Primary>minus", "<Primary>KP_Subtract", "ZoomOut", NULL } },
+  { "zoom.zoom-one",      { "<Primary>0", "<Primary>KP_0", NULL } },
+  { "win.new-tab",        { "<Primary>t", NULL } },
+  { "win.close-tab",      { "<Primary>w", NULL } },
+  { "win.switch-tab(1)",  { "<Alt>1", NULL } },
+  { "win.switch-tab(2)",  { "<Alt>2", NULL } },
+  { "win.switch-tab(3)",  { "<Alt>3", NULL } },
+  { "win.switch-tab(4)",  { "<Alt>4", NULL } },
+  { "win.switch-tab(5)",  { "<Alt>5", NULL } },
+  { "win.switch-tab(6)",  { "<Alt>6", NULL } },
+  { "win.switch-tab(7)",  { "<Alt>7", NULL } },
+  { "win.switch-tab(8)",  { "<Alt>8", NULL } },
+  { "win.switch-tab(9)",  { "<Alt>9", NULL } },
   { NULL }
 };
 
@@ -70,29 +81,23 @@ sysprof_application_activate (GApplication *app)
 
 static void
 sysprof_application_open (GApplication  *app,
-                     GFile        **files,
-                     gint           n_files,
-                     const gchar   *hint)
+                          GFile        **files,
+                          gint           n_files,
+                          const gchar   *hint)
 {
-  guint opened = 0;
-  gint i;
+  GtkWidget *window;
 
   g_assert (SYSPROF_IS_APPLICATION (app));
   g_assert (files != NULL || n_files == 0);
 
-  for (i = 0; i < n_files; i++)
-    {
-      SysprofWindow *window;
+  window = sysprof_window_new (SYSPROF_APPLICATION (app));
 
-      window = g_object_new (SYSPROF_TYPE_WINDOW,
-                             "application", app,
-                             NULL);
-      sysprof_window_open (window, files [i]);
-      gtk_window_present (GTK_WINDOW (window));
-      opened++;
-    }
+  for (gint i = 0; i < n_files; i++)
+    sysprof_window_open (SYSPROF_WINDOW (window), files[i]);
+
+  gtk_window_present (GTK_WINDOW (window));
 
-  if (opened == 0)
+  if (n_files == 0)
     sysprof_application_activate (app);
 }
 
@@ -246,7 +251,6 @@ sysprof_open_capture (GSimpleAction *action,
                       gpointer       user_data)
 {
   GtkApplication *app = user_data;
-  GtkWidget *window;
   GList *list;
 
   g_assert (G_IS_APPLICATION (app));
@@ -257,29 +261,11 @@ sysprof_open_capture (GSimpleAction *action,
 
   for (; list != NULL; list = list->next)
     {
-      window = list->data;
+      GtkWindow *window = list->data;
 
       if (SYSPROF_IS_WINDOW (window))
-        {
-          SysprofWindowState state;
-
-          state = sysprof_window_get_state (SYSPROF_WINDOW (window));
-
-          if (state == SYSPROF_WINDOW_STATE_EMPTY)
-            {
-              sysprof_window_open_from_dialog (SYSPROF_WINDOW (window));
-              return;
-            }
-        }
+        sysprof_window_open_from_dialog (SYSPROF_WINDOW (window));
     }
-
-  window = g_object_new (SYSPROF_TYPE_WINDOW,
-                         "application", app,
-                         NULL);
-
-  gtk_window_present (GTK_WINDOW (window));
-
-  sysprof_window_open_from_dialog (SYSPROF_WINDOW (window));
 }
 
 static void
diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c
index ca950ba..249637b 100644
--- a/src/sysprof/sysprof-window.c
+++ b/src/sysprof/sysprof-window.c
@@ -1,6 +1,6 @@
 /* sysprof-window.c
  *
- * Copyright 2016 Christian Hergert <chergert redhat com>
+ * Copyright 2016-2019 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
@@ -14,791 +14,84 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
 #define G_LOG_DOMAIN "sysprof-window"
 
 #include "config.h"
 
-#include <errno.h>
-#include <glib/gi18n.h>
-#include <math.h>
 #include <sysprof-ui.h>
 
-#include "sysprof-application.h"
 #include "sysprof-window.h"
-#include "sysprof-window-settings.h"
 
 struct _SysprofWindow
 {
   GtkApplicationWindow  parent_instance;
-
-  SysprofWindowState         state;
-
-  SysprofProfiler           *profiler;
-  SysprofCaptureReader      *reader;
-
-  GCancellable              *refilter_cancellable;
-
-  /* Gtk widget template children */
-  SysprofCallgraphView      *callgraph_view;
-  SysprofEmptyStateView     *empty_view;
-  GtkMenuButton             *gear_menu_button;
-  GtkInfoBar                *info_bar;
-  GtkLabel                  *info_bar_label;
-  GtkRevealer               *info_bar_revealer;
-  SysprofMarksView          *marks_view;
-  GtkPaned                  *paned;
-  SysprofProfilerMenuButton *profiler_menu_button;
-  SysprofRecordingStateView *recording_view;
-  GtkButton                 *record_button;
-  GtkLabel                  *subtitle;
-  GtkLabel                  *stat_label;
-  GtkLabel                  *title;
-  GtkStack                  *view_stack;
-  SysprofVisualizerView     *visualizers;
-  SysprofZoomManager        *zoom_manager;
-  GtkLabel                  *zoom_one_label;
-
-  guint                      stats_handler;
-
-  guint                      closing : 1;
+  SysprofNotebook      *notebook;
 };
 
 G_DEFINE_TYPE (SysprofWindow, sysprof_window, GTK_TYPE_APPLICATION_WINDOW)
 
-static void sysprof_window_set_state (SysprofWindow      *self,
-                                 SysprofWindowState  state);
-
-enum {
-  START_RECORDING,
-  STOP_RECORDING,
-  N_SIGNALS
-};
-
-static guint signals [N_SIGNALS];
-
-static void sysprof_window_set_profiler (SysprofWindow   *self,
-                                    SysprofProfiler *profiler);
-
-static G_GNUC_PRINTF(3, 4) void
-sysprof_window_notify_user (SysprofWindow       *self,
-                       GtkMessageType  message_type,
-                       const gchar    *format,
-                       ...)
-{
-  g_autofree gchar *str = NULL;
-  va_list args;
-
-  g_assert (SYSPROF_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
-sysprof_window_action_set (SysprofWindow    *self,
-                      const gchar *action_name,
-                      const gchar *first_property,
-                      ...)
-{
-  gpointer action;
-  va_list args;
-
-  g_assert (SYSPROF_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
-sysprof_window_update_stats (gpointer data)
-{
-  SysprofWindow *self = data;
-
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  if (self->profiler != NULL)
-    {
-      SysprofCaptureWriter *writer;
-
-      if (NULL != (writer = sysprof_profiler_get_writer (self->profiler)))
-        {
-          g_autofree gchar *str = NULL;
-          SysprofCaptureStat stbuf;
-          guint count;
-
-          sysprof_capture_writer_stat (writer, &stbuf);
-
-          count = stbuf.frame_count[SYSPROF_CAPTURE_FRAME_SAMPLE];
-          /* Translators: %u is the number (amount) of samples. */
-          str = g_strdup_printf (_("Samples: %u"), count);
-          gtk_label_set_label (self->stat_label, str);
-        }
-    }
-
-  return G_SOURCE_CONTINUE;
-}
-
-
-static void
-sysprof_window_update_subtitle (SysprofWindow *self)
-{
-  g_autofree gchar *relative = NULL;
-  const gchar *filename;
-  const gchar *date;
-  GTimeVal tv;
-
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (self->reader != NULL);
-
-  if (NULL != (filename = sysprof_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 = sysprof_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");
-
-      /* Translators: The first %s is a file name, the second is the date and time. */
-      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
-sysprof_window_build_profile_cb (GObject      *object,
-                            GAsyncResult *result,
-                            gpointer      user_data)
-{
-  SysprofProfile *profile = (SysprofProfile *)object;
-  g_autoptr(SysprofWindow) self = user_data;
-  g_autoptr(GError) error = NULL;
-
-  g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (profile));
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (G_IS_ASYNC_RESULT (result));
-
-  gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE);
-
-  if (!sysprof_profile_generate_finish (profile, result, &error))
-    {
-      /* If we were cancelled while updating the selection, ignore the failure */
-      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
-          (self->state == SYSPROF_WINDOW_STATE_BROWSING))
-        return;
-      sysprof_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", error->message);
-      sysprof_window_set_state (self, SYSPROF_WINDOW_STATE_EMPTY);
-      return;
-    }
-
-  sysprof_callgraph_view_set_profile (self->callgraph_view, SYSPROF_CALLGRAPH_PROFILE (profile));
-  if (sysprof_callgraph_view_get_n_functions (self->callgraph_view) == 0)
-    sysprof_window_notify_user (self,
-                           GTK_MESSAGE_WARNING,
-                           _("Not enough samples were collected to generate a callgraph"));
-
-  sysprof_visualizer_view_set_reader (self->visualizers, self->reader);
-
-  sysprof_window_set_state (self, SYSPROF_WINDOW_STATE_BROWSING);
-}
-
-static void
-sysprof_window_build_profile (SysprofWindow *self)
-{
-  g_autoptr(SysprofProfile) profile = NULL;
-  SysprofSelection *selection;
-
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (self->reader != NULL);
-
-  if (self->refilter_cancellable != NULL)
-    {
-      if (!g_cancellable_is_cancelled (self->refilter_cancellable))
-        g_cancellable_cancel (self->refilter_cancellable);
-      g_clear_object (&self->refilter_cancellable);
-    }
-
-  selection = sysprof_visualizer_view_get_selection (self->visualizers);
-
-  profile = sysprof_callgraph_profile_new_with_selection (selection);
-  sysprof_profile_set_reader (profile, self->reader);
-
-  self->refilter_cancellable = g_cancellable_new ();
-
-  gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), FALSE);
-
-  sysprof_profile_generate (profile,
-                            self->refilter_cancellable,
-                            sysprof_window_build_profile_cb,
-                            g_object_ref (self));
-
-  sysprof_marks_view_load_async (self->marks_view, self->reader, selection, NULL, NULL, NULL);
-}
-
-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
-sysprof_window_set_state (SysprofWindow      *self,
-                     SysprofWindowState  state)
-{
-  g_autoptr(SysprofProfiler) profiler = NULL;
-
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  if (self->state == state)
-    return;
-
-  self->state = state;
-
-  switch (state)
-    {
-    case SYSPROF_WINDOW_STATE_EMPTY:
-    case SYSPROF_WINDOW_STATE_FAILED:
-      /* Translators: This is a button. */
-      gtk_button_set_label (self->record_button, _("Record"));
-      gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE);
-      add_class (self->record_button, "suggested-action");
-      remove_class (self->record_button, "destructive-action");
-      if (state == SYSPROF_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"));
-      sysprof_callgraph_view_set_profile (self->callgraph_view, NULL);
-      gtk_widget_set_visible (GTK_WIDGET (self->stat_label), FALSE);
-      g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
-      sysprof_window_action_set (self, "close-capture", "enabled", FALSE, NULL);
-      sysprof_window_action_set (self, "save-capture", "enabled", FALSE, NULL);
-      sysprof_window_action_set (self, "screenshot", "enabled", FALSE, NULL);
-      profiler = sysprof_local_profiler_new ();
-      sysprof_window_set_profiler (self, profiler);
-      break;
-
-    case SYSPROF_WINDOW_STATE_RECORDING:
-      /* Translators: This is a button. */
-      gtk_button_set_label (self->record_button, _("Stop"));
-      gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE);
-      remove_class (self->record_button, "suggested-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, sysprof_capture_reader_unref);
-      sysprof_callgraph_view_set_profile (self->callgraph_view, NULL);
-      sysprof_window_action_set (self, "close-capture", "enabled", FALSE, NULL);
-      sysprof_window_action_set (self, "save-capture", "enabled", FALSE, NULL);
-      sysprof_window_action_set (self, "screenshot", "enabled", FALSE, NULL);
-      break;
-
-    case SYSPROF_WINDOW_STATE_PROCESSING:
-      gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), FALSE);
-      gtk_label_set_label (self->subtitle, _("Building profile…"));
-      sysprof_window_action_set (self, "close-capture", "enabled", FALSE, NULL);
-      sysprof_window_action_set (self, "save-capture", "enabled", FALSE, NULL);
-      sysprof_window_action_set (self, "screenshot", "enabled", FALSE, NULL);
-      sysprof_window_build_profile (self);
-      break;
-
-    case SYSPROF_WINDOW_STATE_BROWSING:
-      /* Translators: This is a button. */
-      gtk_button_set_label (self->record_button, _("Record"));
-      gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE);
-      add_class (self->record_button, "suggested-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");
-      sysprof_window_update_stats (self);
-      sysprof_window_update_subtitle (self);
-      sysprof_window_action_set (self, "close-capture", "enabled", TRUE, NULL);
-      sysprof_window_action_set (self, "save-capture", "enabled", TRUE, NULL);
-      sysprof_window_action_set (self, "screenshot", "enabled", TRUE, NULL);
-      profiler = sysprof_local_profiler_new ();
-      sysprof_window_set_profiler (self, profiler);
-      break;
-
-    case SYSPROF_WINDOW_STATE_0:
-    default:
-      g_warning ("Unknown state: %0d", state);
-      break;
-    }
-}
-
-static void
-sysprof_window_enable_stats (SysprofWindow *self)
-{
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  if (self->stats_handler == 0)
-    self->stats_handler =
-      g_timeout_add_seconds (1, sysprof_window_update_stats, self);
-}
-
-static void
-sysprof_window_disable_stats (SysprofWindow *self)
-{
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  if (self->stats_handler != 0)
-    {
-      g_source_remove (self->stats_handler);
-      self->stats_handler = 0;
-    }
-}
-
-static void
-sysprof_window_add_sources (SysprofWindow   *window,
-                       SysprofProfiler *profiler)
-{
-#ifdef __linux__
-  g_autoptr(SysprofSource) host_source = NULL;
-  g_autoptr(SysprofSource) proc_source = NULL;
-  g_autoptr(SysprofSource) perf_source = NULL;
-  g_autoptr(SysprofSource) memory_source = NULL;
-
-  g_assert (SYSPROF_IS_WINDOW (window));
-  g_assert (SYSPROF_IS_PROFILER (profiler));
-
-  proc_source = sysprof_proc_source_new ();
-  sysprof_profiler_add_source (profiler, proc_source);
-
-  perf_source = sysprof_perf_source_new ();
-  sysprof_profiler_add_source (profiler, perf_source);
-
-  host_source = sysprof_hostinfo_source_new ();
-  sysprof_profiler_add_source (profiler, host_source);
-
-  memory_source = sysprof_memory_source_new ();
-  sysprof_profiler_add_source (profiler, memory_source);
-#endif
-}
-
-static void
-sysprof_window_start_recording (SysprofWindow *self)
-{
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  if ((self->state == SYSPROF_WINDOW_STATE_EMPTY) ||
-      (self->state == SYSPROF_WINDOW_STATE_FAILED) ||
-      (self->state == SYSPROF_WINDOW_STATE_BROWSING))
-    {
-      gtk_revealer_set_reveal_child (self->info_bar_revealer, FALSE);
-      sysprof_window_add_sources (self, self->profiler);
-      sysprof_window_set_state (self, SYSPROF_WINDOW_STATE_RECORDING);
-      sysprof_window_enable_stats (self);
-      sysprof_profiler_start (self->profiler);
-      return;
-    }
-}
-
-static void
-sysprof_window_stop_recording (SysprofWindow *self)
-{
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  if (self->state == SYSPROF_WINDOW_STATE_RECORDING)
-    {
-      if (self->profiler != NULL)
-        {
-          /* SysprofProfiler::stopped will move us to generating */
-          gtk_label_set_label (self->subtitle, _("Stopping…"));
-          /*
-           * In case that ::stopped takes a while to execute,
-           * disable record button immediately.
-           */
-          gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), FALSE);
-          sysprof_profiler_stop (self->profiler);
-        }
-    }
-}
-
-static void
-sysprof_window_hide_info_bar_revealer (SysprofWindow *self)
-{
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  gtk_revealer_set_reveal_child (self->info_bar_revealer, FALSE);
-}
-
-static void
-sysprof_window_profiler_stopped (SysprofWindow   *self,
-                            SysprofProfiler *profiler)
-{
-  g_autoptr(SysprofCaptureReader) reader = NULL;
-  g_autoptr(GError) error = NULL;
-  SysprofCaptureWriter *writer;
-
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (SYSPROF_IS_PROFILER (profiler));
-
-  sysprof_window_disable_stats (self);
-
-  if (self->closing)
-    {
-      gtk_window_close (GTK_WINDOW (self));
-      return;
-    }
-
-  if (self->state == SYSPROF_WINDOW_STATE_FAILED)
-    return;
-
-  writer = sysprof_profiler_get_writer (profiler);
-  reader = sysprof_capture_writer_create_reader (writer, &error);
-
-  if (reader == NULL)
-    {
-      sysprof_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", error->message);
-      sysprof_window_set_state (self, SYSPROF_WINDOW_STATE_EMPTY);
-      return;
-    }
-
-  g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
-  self->reader = g_steal_pointer (&reader);
-
-  sysprof_window_build_profile (self);
-}
-
-static void
-sysprof_window_profiler_failed (SysprofWindow   *self,
-                                const GError    *reason,
-                                SysprofProfiler *profiler)
-{
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (reason != NULL);
-  g_assert (SYSPROF_IS_PROFILER (profiler));
-
-  sysprof_window_notify_user (self, GTK_MESSAGE_ERROR, "%s", reason->message);
-  sysprof_window_set_state (self, SYSPROF_WINDOW_STATE_FAILED);
-}
-
-static void
-sysprof_window_set_profiler (SysprofWindow   *self,
-                             SysprofProfiler *profiler)
+/**
+ * sysprof_window_new:
+ *
+ * Create a new #SysprofWindow.
+ *
+ * Returns: (transfer full): a newly created #SysprofWindow
+ */
+GtkWidget *
+sysprof_window_new (SysprofApplication *application)
 {
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (SYSPROF_IS_PROFILER (profiler));
-
-  if (self->profiler != profiler)
-    {
-      if (self->profiler != NULL)
-        {
-          if (sysprof_profiler_get_is_running (self->profiler))
-            sysprof_profiler_stop (self->profiler);
-          sysprof_profiler_menu_button_set_profiler (self->profiler_menu_button, NULL);
-          sysprof_recording_state_view_set_profiler (self->recording_view, NULL);
-          g_clear_object (&self->profiler);
-        }
-
-      if (profiler != NULL)
-        {
-          if (!sysprof_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 (sysprof_window_profiler_stopped),
-                                   self,
-                                   G_CONNECT_SWAPPED);
-
-          g_signal_connect_object (profiler,
-                                   "failed",
-                                   G_CALLBACK (sysprof_window_profiler_failed),
-                                   self,
-                                   G_CONNECT_SWAPPED);
-
-          sysprof_profiler_menu_button_set_profiler (self->profiler_menu_button, profiler);
-          sysprof_recording_state_view_set_profiler (self->recording_view, profiler);
-        }
-    }
+  return g_object_new (SYSPROF_TYPE_WINDOW,
+                       "application", application,
+                       NULL);
 }
 
 static void
-sysprof_window_open_capture (GSimpleAction *action,
-                             GVariant      *variant,
-                             gpointer       user_data)
+new_tab_cb (GSimpleAction *action,
+            GVariant      *param,
+            gpointer       user_data)
 {
   SysprofWindow *self = user_data;
 
-  g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (variant == NULL);
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  sysprof_window_open_from_dialog (self);
-}
-
-static void
-sysprof_window_save_capture (GSimpleAction *action,
-                             GVariant      *variant,
-                             gpointer       user_data)
-{
-  g_autoptr(SysprofCaptureReader) reader = NULL;
-  SysprofWindow *self = user_data;
-  GtkFileChooserNative *dialog;
-  gint response;
-
-  g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (variant == NULL);
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  if (self->reader == NULL)
-    {
-      g_warning ("Save called without a capture open, ignoring");
-      return;
-    }
-
-  reader = sysprof_capture_reader_ref (self->reader);
-
-  /* Translators: This is a window title. */
-  dialog = gtk_file_chooser_native_new (_("Save Capture As…"),
-                                        GTK_WINDOW (self),
-                                        GTK_FILE_CHOOSER_ACTION_SAVE,
-                                        /* Translators: This is a button. */
-                                        _("Save"),
-                                        /* Translators: This is a button. */
-                                        _("Cancel"));
-
-  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE);
-
-  response = gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog));
-
-  if (response == GTK_RESPONSE_ACCEPT)
-    {
-      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 (!sysprof_capture_reader_save_as (reader, filename, &error))
-        {
-          sysprof_window_notify_user (self,
-                                 GTK_MESSAGE_ERROR,
-                                 /* Translators: %s is the error message. */
-                                 _("An error occurred while attempting to save your capture: %s"),
-                                 error->message);
-          goto failure;
-        }
-    }
+  g_return_if_fail (SYSPROF_IS_WINDOW (self));
 
-failure:
-  gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (dialog));
+  sysprof_window_new_tab (self);
 }
 
 static void
-sysprof_window_close_capture (GSimpleAction *action,
-                              GVariant      *variant,
-                              gpointer       user_data)
+switch_tab_cb (GSimpleAction *action,
+               GVariant      *param,
+               gpointer       user_data)
 {
   SysprofWindow *self = user_data;
+  gint page;
 
-  g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (variant == NULL);
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  sysprof_window_set_state (self, SYSPROF_WINDOW_STATE_EMPTY);
-}
-
-static void
-sysprof_window_record_button_clicked (SysprofWindow *self,
-                                      GtkButton     *button)
-{
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (GTK_IS_BUTTON (button));
+  g_return_if_fail (SYSPROF_IS_WINDOW (self));
+  g_return_if_fail (g_variant_is_of_type (param, G_VARIANT_TYPE_INT32));
 
-  if (self->state == SYSPROF_WINDOW_STATE_RECORDING)
-    sysprof_window_stop_recording (self);
-  else
-    sysprof_window_start_recording (self);
+  page = g_variant_get_int32 (param);
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook), page - 1);
 }
 
 static void
-sysprof_window_screenshot (GSimpleAction *action,
-                           GVariant      *variant,
-                           gpointer       user_data)
+close_tab_cb (GSimpleAction *action,
+              GVariant      *param,
+              gpointer       user_data)
 {
   SysprofWindow *self = user_data;
-  g_autofree gchar *str = NULL;
-  GtkWindow *window;
-  GtkScrolledWindow *scroller;
-  GtkTextView *text_view;
-
-  g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  if (NULL == (str = sysprof_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
-sysprof_window_delete_event (GtkWidget   *widget,
-                        GdkEventAny *event)
-{
-  SysprofWindow *self = (SysprofWindow *)widget;
 
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (event != NULL);
-
-  if (self->state == SYSPROF_WINDOW_STATE_RECORDING)
-    {
-      if (self->profiler != NULL)
-        {
-          if (self->closing == FALSE)
-            {
-              self->closing = TRUE;
-              sysprof_profiler_stop (self->profiler);
-              return GDK_EVENT_STOP;
-            }
-        }
-    }
-
-  return GDK_EVENT_PROPAGATE;
-}
-
-static gboolean
-zoom_level_to_string (GBinding     *binding,
-                      const GValue *from_value,
-                      GValue       *to_value,
-                      gpointer      user_data)
-{
-  gdouble percent = 100.0 * g_value_get_double (from_value);
-  g_value_take_string (to_value, g_strdup_printf ("%u%%", (guint)floor (percent)));
-  return TRUE;
-}
-
-static void
-sysprof_window_visualizers_selection_changed (SysprofWindow              *self,
-                                         SysprofSelection *selection)
-{
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (SYSPROF_IS_SELECTION (selection));
-
-  sysprof_window_build_profile (self);
-}
-
-static void
-sysprof_window_destroy (GtkWidget *widget)
-{
-  SysprofWindow *self = (SysprofWindow *)widget;
-
-  if (self->refilter_cancellable != NULL)
-    {
-      if (!g_cancellable_is_cancelled (self->refilter_cancellable))
-        g_cancellable_cancel (self->refilter_cancellable);
-      g_clear_object (&self->refilter_cancellable);
-    }
-
-  g_clear_object (&self->profiler);
-  g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
-  sysprof_window_disable_stats (self);
+  g_return_if_fail (SYSPROF_IS_WINDOW (self));
 
-  GTK_WIDGET_CLASS (sysprof_window_parent_class)->destroy (widget);
+  sysprof_notebook_close_current (self->notebook);
 }
 
 static void
-sysprof_window_constructed (GObject *object)
+sysprof_window_finalize (GObject *object)
 {
-  SysprofWindow *self = (SysprofWindow *)object;
-  g_autoptr(SysprofProfiler) profiler = NULL;
-
-  G_OBJECT_CLASS (sysprof_window_parent_class)->constructed (object);
-
-  profiler = sysprof_local_profiler_new ();
-  sysprof_window_set_profiler (self, profiler);
-
-  sysprof_window_set_state (self, SYSPROF_WINDOW_STATE_EMPTY);
+  G_OBJECT_CLASS (sysprof_window_parent_class)->finalize (object);
 }
 
 static void
@@ -806,255 +99,61 @@ sysprof_window_class_init (SysprofWindowClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-  GtkBindingSet *binding_set;
-
-  object_class->constructed = sysprof_window_constructed;
-
-  widget_class->delete_event = sysprof_window_delete_event;
-  widget_class->destroy = sysprof_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 (sysprof_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 (sysprof_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);
+  object_class->finalize = sysprof_window_finalize;
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-window.ui");
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, callgraph_view);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, empty_view);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, gear_menu_button);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, info_bar);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, info_bar_label);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, info_bar_revealer);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, marks_view);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, paned);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, profiler_menu_button);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, record_button);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, recording_view);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, stat_label);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, subtitle);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, title);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, view_stack);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, visualizers);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, zoom_manager);
-  gtk_widget_class_bind_template_child (widget_class, SysprofWindow, zoom_one_label);
+  gtk_widget_class_bind_template_child (widget_class, SysprofWindow , notebook);
 
-  g_type_ensure (SYSPROF_TYPE_MARKS_VIEW);
+  g_type_ensure (SYSPROF_TYPE_PROFILER_MENU_BUTTON);
+  g_type_ensure (SYSPROF_TYPE_NOTEBOOK);
+  g_type_ensure (SYSPROF_TYPE_DISPLAY);
 }
 
 static void
 sysprof_window_init (SysprofWindow *self)
 {
-  static GActionEntry action_entries[] = {
-    { "close-capture", sysprof_window_close_capture },
-    { "open-capture",  sysprof_window_open_capture },
-    { "save-capture",  sysprof_window_save_capture },
-    { "screenshot",  sysprof_window_screenshot },
+  static GActionEntry actions[] = {
+    { "close-tab", close_tab_cb },
+    { "new-tab", new_tab_cb },
+    { "switch-tab", switch_tab_cb, "i" },
   };
-  SysprofSelection *selection;
-  g_autoptr(GtkWindowGroup) window_group = NULL;
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-#ifdef DEVELOPMENT_BUILD
-  gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "development-version");
-#endif
-
-  /*
-   * Hookup widget signals.
-   */
-
-  g_signal_connect_object (self->info_bar,
-                           "response",
-                           G_CALLBACK (sysprof_window_hide_info_bar_revealer),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  g_signal_connect_object (self->info_bar,
-                           "close",
-                           G_CALLBACK (sysprof_window_hide_info_bar_revealer),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  g_signal_connect_object (self->record_button,
-                           "clicked",
-                           G_CALLBACK (sysprof_window_record_button_clicked),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  g_object_bind_property_full (self->zoom_manager, "zoom", self->zoom_one_label, "label",
-                               G_BINDING_SYNC_CREATE,
-                               zoom_level_to_string, NULL, NULL, NULL);
-
-  /*
-   * Wire up selections for visualizers to update callgraph.
-   */
-
-  selection = sysprof_visualizer_view_get_selection (self->visualizers);
-
-  g_signal_connect_object (selection,
-                           "changed",
-                           G_CALLBACK (sysprof_window_visualizers_selection_changed),
-                           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),
+                                   actions,
+                                   G_N_ELEMENTS (actions),
                                    self);
-  gtk_widget_insert_action_group (GTK_WIDGET (self), "zoom", G_ACTION_GROUP (self->zoom_manager));
-
-  /*
-   * Restore previous window settings.
-   */
-  sysprof_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));
-
-  /*
-   * Prevent grabs (e.g. modal dialogs) from affecting multiple windows.
-   */
-  window_group = gtk_window_group_new ();
-  gtk_window_group_add_window (window_group, GTK_WINDOW (self));
-}
-
-static void
-sysprof_window_open_cb (GObject      *object,
-                   GAsyncResult *result,
-                   gpointer      user_data)
-{
-  SysprofWindow *self = (SysprofWindow *)object;
-  g_autoptr(SysprofCaptureReader) reader = NULL;
-  g_autoptr(GError) error = NULL;
-
-  g_assert (SYSPROF_IS_WINDOW (self));
-  g_assert (G_IS_TASK (result));
-
-  if (!(reader = g_task_propagate_pointer (G_TASK (result), &error)))
-    {
-      sysprof_window_notify_user (self,
-                                  GTK_MESSAGE_ERROR,
-                                  "%s", error->message);
-      return;
-    }
-
-  g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
-  self->reader = g_steal_pointer (&reader);
-
-  sysprof_window_set_state (self, SYSPROF_WINDOW_STATE_PROCESSING);
-}
-
-static void
-sysprof_window_open_worker (GTask        *task,
-                            gpointer      source_object,
-                            gpointer      task_data,
-                            GCancellable *cancellable)
-{
-  g_autofree gchar *path = NULL;
-  g_autoptr(GError) error = NULL;
-  SysprofCaptureReader *reader;
-  GFile *file = task_data;
-
-  g_assert (G_IS_TASK (task));
-  g_assert (SYSPROF_IS_WINDOW (source_object));
-  g_assert (G_IS_FILE (file));
-
-  path = g_file_get_path (file);
-
-  if (!(reader = sysprof_capture_reader_new (path, &error)))
-    g_task_return_error (task, g_steal_pointer (&error));
-  else
-    g_task_return_pointer (task,
-                           g_steal_pointer (&reader),
-                           (GDestroyNotify)sysprof_capture_reader_unref);
 }
 
 void
 sysprof_window_open (SysprofWindow *self,
                      GFile         *file)
 {
-  g_autoptr(GTask) task = NULL;
-
   g_return_if_fail (SYSPROF_IS_WINDOW (self));
   g_return_if_fail (G_IS_FILE (file));
 
-  if (!g_file_is_native (file))
-    {
-      sysprof_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, sysprof_window_open_cb, NULL);
-  g_task_set_task_data (task, g_object_ref (file), g_object_unref);
-  g_task_run_in_thread (task, sysprof_window_open_worker);
 }
 
-SysprofWindowState
-sysprof_window_get_state (SysprofWindow *self)
+void
+sysprof_window_open_from_dialog (SysprofWindow *self)
 {
-  g_return_val_if_fail (SYSPROF_IS_WINDOW (self), SYSPROF_WINDOW_STATE_0);
-
-  return self->state;
+  g_return_if_fail (SYSPROF_IS_WINDOW (self));
 }
 
 void
-sysprof_window_open_from_dialog (SysprofWindow *self)
+sysprof_window_new_tab (SysprofWindow *self)
 {
-  GtkFileChooserNative *dialog;
-  GtkFileFilter *filter;
-  gint response;
-
-  g_assert (SYSPROF_IS_WINDOW (self));
-
-  /* Translators: This is a window title. */
-  dialog = gtk_file_chooser_native_new (_("Open Capture…"),
-                                        GTK_WINDOW (self),
-                                        GTK_FILE_CHOOSER_ACTION_OPEN,
-                                        /* Translators: This is a button. */
-                                        _("Open"),
-                                        /* Translators: This is a button. */
-                                        _("Cancel"));
-
-  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);
+  GtkWidget *display;
+  gint page;
 
-  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);
-
-  response = gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog));
-
-  if (response == GTK_RESPONSE_ACCEPT)
-    {
-      g_autoptr(GFile) file = NULL;
+  g_return_if_fail (SYSPROF_IS_WINDOW (self));
 
-      file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
-      sysprof_window_open (self, file);
-    }
+  display = sysprof_display_new ();
+  page = gtk_notebook_insert_page (GTK_NOTEBOOK (self->notebook), display, NULL, -1);
+  gtk_widget_show (display);
 
-  gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (dialog));
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook), page);
 }
diff --git a/src/sysprof/sysprof-window.h b/src/sysprof/sysprof-window.h
index ca0ceb9..1b88aea 100644
--- a/src/sysprof/sysprof-window.h
+++ b/src/sysprof/sysprof-window.h
@@ -1,6 +1,6 @@
 /* sysprof-window.h
  *
- * Copyright 2016 Christian Hergert <chergert redhat com>
+ * Copyright 2016-2019 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
@@ -14,39 +14,26 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#ifndef SYSPROF_WINDOW_H
-#define SYSPROF_WINDOW_H
+#pragma once
 
 #include <gtk/gtk.h>
 
+#include "sysprof-application.h"
+
 G_BEGIN_DECLS
 
 #define SYSPROF_TYPE_WINDOW (sysprof_window_get_type())
 
 G_DECLARE_FINAL_TYPE (SysprofWindow, sysprof_window, SYSPROF, WINDOW, GtkApplicationWindow)
 
-typedef enum
-{
-  SYSPROF_WINDOW_STATE_0,
-  SYSPROF_WINDOW_STATE_EMPTY,
-  SYSPROF_WINDOW_STATE_FAILED,
-  SYSPROF_WINDOW_STATE_RECORDING,
-  SYSPROF_WINDOW_STATE_PROCESSING,
-  SYSPROF_WINDOW_STATE_BROWSING,
-} SysprofWindowState;
-
-SysprofWindowState  sysprof_window_get_state          (SysprofWindow  *self);
-gboolean       sysprof_window_get_recording      (SysprofWindow  *self);
-GFile         *sysprof_window_get_capture_file   (SysprofWindow  *self);
-void           sysprof_window_set_capture_file   (SysprofWindow  *self,
-                                             GFile     *capture_file);
-void           sysprof_window_open               (SysprofWindow  *self,
-                                             GFile     *file);
-void           sysprof_window_open_from_dialog   (SysprofWindow  *self);
+GtkWidget *sysprof_window_new              (SysprofApplication *application);
+void       sysprof_window_new_tab          (SysprofWindow      *self);
+void       sysprof_window_open             (SysprofWindow      *self,
+                                            GFile              *file);
+void       sysprof_window_open_from_dialog (SysprofWindow      *self);
 
 G_END_DECLS
-
-#endif /* SYSPROF_WINDOW_H */
-
diff --git a/src/sysprof/ui/sysprof-window.ui b/src/sysprof/ui/sysprof-window.ui
index b92247d..a5e85db 100644
--- a/src/sysprof/ui/sysprof-window.ui
+++ b/src/sysprof/ui/sysprof-window.ui
@@ -70,296 +70,17 @@
             <property name="position">1</property>
           </packing>
         </child>
-        <child>
-          <object class="GtkMenuButton" id="gear_menu_button">
-            <property name="popover">gear_popover</property>
-            <property name="visible">true</property>
-            <style>
-              <class name="image-button"/>
-            </style>
-            <child>
-              <object class="GtkImage">
-                <property name="icon-name">open-menu-symbolic</property>
-                <property name="visible">true</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="pack-type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
       </object>
     </child>
     <child>
-      <object class="GtkBox">
-        <property name="orientation">vertical</property>
+      <object class="SysprofNotebook" id="notebook">
         <property name="visible">true</property>
         <child>
-          <object class="GtkRevealer" id="info_bar_revealer">
+          <object class="SysprofDisplay">
             <property name="visible">true</property>
-            <property name="reveal-child">false</property>
-            <child>
-              <object class="GtkInfoBar" id="info_bar">
-                <property name="visible">true</property>
-                <child internal-child="content_area">
-                  <object class="GtkBox">
-                    <child>
-                      <object class="GtkLabel" id="info_bar_label">
-                        <property name="hexpand">true</property>
-                        <property name="label">Failure</property>
-                        <property name="visible">true</property>
-                        <property name="wrap">true</property>
-                        <property name="xalign">0</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkButton" id="close_info_button">
-                        <property name="label" translatable="yes" comments="Translators: This is a 
button.">_Close</property>
-                        <property name="use-underline">true</property>
-                        <property name="visible">true</property>
-                        <property name="width-request">100</property>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-                <action-widgets>
-                  <action-widget response="0">close_info_button</action-widget>
-                </action-widgets>
-              </object>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="GtkStack" id="view_stack">
-            <property name="expand">true</property>
-            <property name="transition-duration">400</property>
-            <property name="transition-type">crossfade</property>
-            <property name="visible">true</property>
-            <child>
-              <object class="SysprofEmptyStateView" id="empty_view">
-                <property name="visible">true</property>
-              </object>
-              <packing>
-                <property name="name">empty</property>
-              </packing>
-            </child>
-            <child>
-              <object class="SysprofFailedStateView" id="failed_view">
-                <property name="visible">true</property>
-              </object>
-              <packing>
-                <property name="name">failed</property>
-              </packing>
-            </child>
-            <child>
-              <object class="SysprofRecordingStateView" id="recording_view">
-                <property name="visible">true</property>
-              </object>
-              <packing>
-                <property name="name">recording</property>
-              </packing>
-            </child>
-            <child>
-              <object class="SysprofMultiPaned" id="paned">
-                <property name="orientation">vertical</property>
-                <property name="visible">true</property>
-                <property name="vexpand">true</property>
-                <child>
-                  <object class="SysprofVisualizerView" id="visualizers">
-                    <property name="visible">true</property>
-                    <property name="zoom-manager">zoom_manager</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkNotebook" id="notebook">
-                    <property name="vexpand">true</property>
-                    <property name="visible">true</property>
-                    <child>
-                      <object class="SysprofCallgraphView" id="callgraph_view">
-                        <property name="visible">true</property>
-                        <property name="vexpand">true</property>
-                      </object>
-                      <packing>
-                        <property name="tab-label" translatable="yes">Callgraph</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="SysprofMarksView" id="marks_view">
-                        <property name="visible">true</property>
-                        <property name="vexpand">true</property>
-                      </object>
-                      <packing>
-                        <property name="tab-label" translatable="yes">Marks</property>
-                      </packing>
-                    </child>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="name">browsing</property>
-              </packing>
-            </child>
           </object>
         </child>
       </object>
     </child>
   </template>
-  <object class="SysprofZoomManager" id="zoom_manager">
-    <property name="min-zoom">0.01</property>
-  </object>
-  <object class="GtkPopover" id="gear_popover">
-    <property name="border-width">12</property>
-    <property name="width-request">225</property>
-    <child>
-      <object class="GtkBox">
-        <property name="orientation">vertical</property>
-        <property name="visible">true</property>
-        <child>
-          <object class="GtkBox">
-            <property name="hexpand">true</property>
-            <property name="orientation">horizontal</property>
-            <property name="visible">true</property>
-            <style>
-              <class name="linked"/>
-            </style>
-            <child>
-              <object class="GtkButton">
-                <property name="action-name">zoom.zoom-out</property>
-                <property name="hexpand">true</property>
-                <property name="tooltip-text" translatable="yes" comments="Translators: This is a 
tooltip.">Zoom out (Ctrl+-)</property>
-                <property name="visible">true</property>
-                <style>
-                  <class name="image-button"/>
-                </style>
-                <child>
-                  <object class="GtkImage">
-                    <property name="icon-name">zoom-out-symbolic</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton">
-                <property name="action-name">zoom.zoom-one</property>
-                <property name="tooltip-text" translatable="yes" comments="Translators: This is a 
tooltip.">Reset zoom level (Ctrl+0)</property>
-                <property name="visible">true</property>
-                <child>
-                  <object class="GtkLabel" id="zoom_one_label">
-                    <property name="width-chars">5</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton">
-                <property name="action-name">zoom.zoom-in</property>
-                <property name="hexpand">true</property>
-                <property name="tooltip-text" translatable="yes" comments="Translators: This is a 
tooltip.">Zoom in (Ctrl++)</property>
-                <property name="visible">true</property>
-                <style>
-                  <class name="image-button"/>
-                </style>
-                <child>
-                  <object class="GtkImage">
-                    <property name="icon-name">zoom-in-symbolic</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="GtkSeparator">
-            <property name="orientation">horizontal</property>
-            <property name="margin-top">6</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="action-name">app.new-window</property>
-            <property name="text" translatable="yes" comments="Translators: This is a menu label.">New 
Window</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkSeparator">
-            <property name="orientation">horizontal</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="action-name">win.open-capture</property>
-            <property name="text" translatable="yes" context="menu label" comments="Translators: This is a 
menu label.">Open Capture…</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="action-name">win.save-capture</property>
-            <property name="text" translatable="yes" comments="Translators: This is a menu label.">Save 
As…</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkSeparator">
-            <property name="orientation">horizontal</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="action-name">win.screenshot</property>
-            <property name="text" translatable="yes" comments="Translators: This is a menu 
label.">Screenshot</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkSeparator">
-            <property name="orientation">horizontal</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="action-name">win.close-capture</property>
-            <property name="text" translatable="yes" comments="Translators: This is a menu 
label.">Close</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkSeparator">
-            <property name="orientation">horizontal</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="action-name">app.show-help-overlay</property>
-            <property name="text" translatable="yes">Keyboard Shortcuts</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="action-name">app.help</property>
-            <property name="text" translatable="yes">Help</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="action-name">app.about</property>
-            <property name="text" translatable="yes">About Sysprof</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
-      </object>
-    </child>
-  </object>
 </interface>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]