[sysprof/wip/visualizers] wip: visualizer work



commit 51ed367b743db8d7efe94aece5df817de42aee14
Author: Christian Hergert <chergert redhat com>
Date:   Tue Oct 4 17:47:37 2016 -0700

    wip: visualizer work

 lib/resources/ui/sp-visualizer-view.ui |   21 +-
 lib/sp-line-visualizer-row.c           |  543 ++++----------------------------
 lib/sp-visualizer-list.c               |  149 +++++-----
 lib/sp-visualizer-list.h               |   18 +-
 lib/sp-visualizer-row.c                |  267 ++++++++++++++--
 lib/sp-visualizer-row.h                |   46 ++--
 lib/sp-visualizer-view.c               |  128 +-------
 7 files changed, 426 insertions(+), 746 deletions(-)
---
diff --git a/lib/resources/ui/sp-visualizer-view.ui b/lib/resources/ui/sp-visualizer-view.ui
index 9970393..45ec522 100644
--- a/lib/resources/ui/sp-visualizer-view.ui
+++ b/lib/resources/ui/sp-visualizer-view.ui
@@ -12,26 +12,13 @@
           </object>
         </child>
         <child>
-          <object class="GtkBox">
-            <property name="orientation">vertical</property>
+          <object class="GtkScrolledWindow">
+            <property name="propagate-natural-height">true</property>
+            <property name="propagate-natural-width">false</property>
             <property name="visible">true</property>
             <child>
-              <object class="GtkScrolledWindow">
-                <property name="hadjustment">scroll_adjustment</property>
-                <property name="propagate-natural-height">true</property>
-                <property name="vexpand">true</property>
+              <object class="SpVisualizerList" id="list">
                 <property name="visible">true</property>
-                <child>
-                  <object class="SpViewport">
-                    <property name="visible">true</property>
-                    <property name="hadjustment">scroll_adjustment</property>
-                    <child>
-                      <object class="SpVisualizerList" id="list">
-                        <property name="visible">true</property>
-                      </object>
-                    </child>
-                  </object>
-                </child>
               </object>
             </child>
           </object>
diff --git a/lib/sp-line-visualizer-row.c b/lib/sp-line-visualizer-row.c
index 5153de3..0a92e5a 100644
--- a/lib/sp-line-visualizer-row.c
+++ b/lib/sp-line-visualizer-row.c
@@ -47,13 +47,6 @@ typedef struct
   PointCache *cache;
 
   /*
-   * This is our offscreen surface that is already rendered. We use it
-   * to render to the front buffer (during our ::draw vfunc).
-   */
-  cairo_surface_t *surface;
-  gint64 surface_begin_time;
-
-  /*
    * Child widget to display the label in the upper corner.
    */
   GtkLabel *label;
@@ -64,10 +57,6 @@ typedef struct
   gdouble y_lower;
   gdouble y_upper;
 
-  /* Cached copy of begin/end time for quick access */
-  gint64 begin_time;
-  gint64 end_time;
-
   /*
    * If we have a new counter discovered or the reader is set, we might
    * want to delay loading until we return to the main loop. This can
@@ -87,17 +76,6 @@ typedef struct
 
 typedef struct
 {
-  PointCache *cache;
-  GArray *lines;
-  GdkRGBA color;
-  gint64 begin_time;
-  gint scale_factor;
-  gint width;
-  gint height;
-} RenderData;
-
-typedef struct
-{
   SpCaptureCursor *cursor;
   GArray *lines;
   PointCache *cache;
@@ -116,15 +94,6 @@ static void            sp_line_visualizer_row_load_data_async   (SpLineVisualize
 static PointCache      *sp_line_visualizer_row_load_data_finish (SpLineVisualizerRow  *self,
                                                                  GAsyncResult         *result,
                                                                  GError              **error);
-static void            sp_line_visualizer_row_render_async      (SpLineVisualizerRow  *self,
-                                                                 PointCache           *cache,
-                                                                 GCancellable         *cancellable,
-                                                                 GAsyncReadyCallback   callback,
-                                                                 gpointer              user_data);
-static cairo_surface_t *sp_line_visualizer_row_render_finish    (SpLineVisualizerRow  *self,
-                                                                 GAsyncResult         *result,
-                                                                 gint64               *surface_begin_time,
-                                                                 GError              **error);
 
 enum {
   PROP_0,
@@ -150,19 +119,6 @@ load_data_free (gpointer data)
     }
 }
 
-static void
-render_data_free (gpointer data)
-{
-  RenderData *render = data;
-
-  if (render != NULL)
-    {
-      g_clear_pointer (&render->cache, point_cache_unref);
-      g_clear_pointer (&render->lines, g_array_unref);
-      g_slice_free (RenderData, render);
-    }
-}
-
 static GArray *
 copy_array (GArray *ar)
 {
@@ -175,151 +131,78 @@ copy_array (GArray *ar)
   return ret;
 }
 
-static void
-sp_line_visualizer_row_render_cb (GObject      *object,
-                                  GAsyncResult *result,
-                                  gpointer      user_data)
+static gboolean
+sp_line_visualizer_row_draw (GtkWidget *widget,
+                             cairo_t   *cr)
 {
-  SpLineVisualizerRow *self = (SpLineVisualizerRow *)object;
+  SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
   SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
-  g_autoptr(GError) error = NULL;
-  cairo_surface_t *surface;
-  gint64 surface_begin_time;
-
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
-  g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (user_data == NULL);
-
-  surface = sp_line_visualizer_row_render_finish (self, result, &surface_begin_time, &error);
-
-  if (surface == NULL)
-    {
-      g_warning ("%s", error->message);
-      return;
-    }
-
-  g_clear_pointer (&priv->surface, cairo_surface_destroy);
+  GtkStyleContext *style_context;
+  GtkStateFlags flags;
+  GdkRGBA foreground;
+  gboolean ret;
 
-  priv->surface = surface;
-  priv->surface_begin_time = surface_begin_time;
+  g_assert (SP_IS_LINE_VISUALIZER_ROW (widget));
+  g_assert (cr != NULL);
 
-  gtk_widget_queue_draw (GTK_WIDGET (self));
-}
+  ret = GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->draw (widget, cr);
 
-static void
-sp_line_visualizer_row_begin_offscreen_draw (SpLineVisualizerRow *self)
-{
-  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
+  if (priv->cache == NULL)
+    return ret;
 
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
+  style_context = gtk_widget_get_style_context (widget);
+  flags = gtk_widget_get_state_flags (widget);
+  gtk_style_context_get_color (style_context, flags, &foreground);
 
-  if (priv->cache != NULL)
-    sp_line_visualizer_row_render_async (self,
-                                         priv->cache,
-                                         NULL,
-                                         sp_line_visualizer_row_render_cb,
-                                         NULL);
-}
+  for (guint line = 0; line < priv->lines->len; line++)
+    {
+      g_autofree SpVisualizerRowAbsolutePoint *points = NULL;
+      const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line);
+      const Point *fpoints;
+      guint n_fpoints = 0;
 
-static inline void
-subtract_border (GtkAllocation *alloc,
-                 GtkBorder     *border)
-{
-#if 0
-  g_print ("Border; %d %d %d %d\n", border->top, border->left, border->bottom, border->right);
-#endif
-
-  alloc->x += border->left;
-  alloc->y += border->top;
-  alloc->width -= border->left + border->right;
-  alloc->height -= border->top + border->bottom;
-}
+      fpoints = point_cache_get_points (priv->cache, line_info->id, &n_fpoints);
 
-static void
-adjust_alloc_for_borders (SpLineVisualizerRow *self,
-                          GtkAllocation       *alloc)
-{
-  GtkStyleContext *style_context;
-  GtkBorder border;
-  GtkStateFlags state;
-
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
-  g_assert (alloc != NULL);
+      if (n_fpoints > 0)
+        {
+          gfloat last_x;
+          gfloat last_y;
 
-  state = gtk_widget_get_state_flags (GTK_WIDGET (self));
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  gtk_style_context_get_border (style_context, state, &border);
+          points = g_new0 (SpVisualizerRowAbsolutePoint, n_fpoints);
 
-  subtract_border (alloc, &border);
-}
+          sp_visualizer_row_translate_points (SP_VISUALIZER_ROW (self),
+                                              (const SpVisualizerRowRelativePoint *)fpoints,
+                                              n_fpoints,
+                                              points,
+                                              n_fpoints);
 
-static inline gdouble
-get_x_for_time (SpLineVisualizerRow *self,
-                const GtkAllocation *alloc,
-                gint64               t)
-{
-  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
-  gint64 timespan = priv->end_time - priv->begin_time;
-  gdouble x_ratio = (gdouble)(t - priv->begin_time) / (gdouble)timespan;
-  return alloc->width * x_ratio;
-}
+          last_x = points[0].x;
+          last_y = points[0].y;
 
-static gboolean
-sp_line_visualizer_row_draw (GtkWidget *widget,
-                             cairo_t   *cr)
-{
-  SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
-  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
-  GtkAllocation alloc;
-  gboolean ret;
+          cairo_move_to (cr, last_x, last_y);
 
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (widget));
-  g_assert (cr != NULL);
+          for (guint i = 1; i < n_fpoints; i++)
+            {
+              cairo_curve_to (cr,
+                              last_x + ((points[i].x - last_x) / 2),
+                              last_y,
+                              last_x + ((points[i].x - last_x) / 2),
+                              points[i].y,
+                              points[i].x,
+                              points[i].y);
+              last_x = points[i].x;
+              last_y = points[i].y;
+            }
 
-  gtk_widget_get_allocation (widget, &alloc);
-  adjust_alloc_for_borders (self, &alloc);
+          cairo_set_line_width (cr, line_info->line_width);
 
-  ret = GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->draw (widget, cr);
+          if (line_info->use_default_style)
+            gdk_cairo_set_source_rgba (cr, &foreground);
+          else
+            gdk_cairo_set_source_rgba (cr, &line_info->foreground);
 
-  if (ret == GDK_EVENT_PROPAGATE && priv->surface != NULL)
-    {
-      gint scale_factor = gtk_widget_get_scale_factor (widget);
-      gint width;
-      gint height;
-      gint x_offset = 0;
-
-      width = cairo_image_surface_get_width (priv->surface) / scale_factor;
-      height = cairo_image_surface_get_height (priv->surface) / scale_factor;
-
-      /*
-       * We must have another threaded draw queued, so to give the impression
-       * that we are quickly growing with the widget, we will scale the draw
-       * and then paint our wrongly sized surface (which will likely be a bit
-       * blurred, but that's okay).
-       */
-      if (width != alloc.width || height != alloc.height)
-        {
-          cairo_rectangle (cr, 0, 0, width, height);
-          cairo_scale (cr, (gdouble)alloc.width / (gdouble)width, (gdouble)alloc.height / (gdouble)height);
-          cairo_set_source_surface (cr, priv->surface, 0, 0);
-          cairo_fill (cr);
-          return ret;
+          cairo_stroke (cr);
         }
-
-      /*
-       * If we have moved our time range since we last rendered, then we
-       * might need to adjust the x_offset when reusing this cached surface.
-       */
-      if (priv->surface_begin_time != priv->begin_time)
-        x_offset = get_x_for_time (self, &alloc, priv->surface_begin_time);
-
-      /*
-       * This is our ideal path, where we have a 1-to-1 match of our backing
-       * surface matching the widgets current allocation.
-       */
-      cairo_rectangle (cr, 0, 0, alloc.width, alloc.height);
-      cairo_set_source_surface (cr, priv->surface, x_offset, 0);
-      cairo_fill (cr);
     }
 
   return ret;
@@ -348,7 +231,7 @@ sp_line_visualizer_row_load_data_cb (GObject      *object,
   g_clear_pointer (&priv->cache, point_cache_unref);
   priv->cache = g_steal_pointer (&cache);
 
-  gtk_widget_queue_resize (GTK_WIDGET (self));
+  gtk_widget_queue_draw (GTK_WIDGET (self));
 }
 
 static gboolean
@@ -432,76 +315,6 @@ sp_line_visualizer_row_finalize (GObject *object)
 }
 
 static void
-sp_line_visualizer_row_size_allocate (GtkWidget     *widget,
-                                      GtkAllocation *alloc)
-{
-  SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
-
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
-  g_assert (alloc != NULL);
-
-  GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->size_allocate (widget, alloc);
-
-  sp_line_visualizer_row_begin_offscreen_draw (self);
-}
-
-static void
-sp_line_visualizer_row_style_updated (GtkWidget *widget)
-{
-  SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
-  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
-
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
-
-  GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->style_updated (widget);
-
-  /*
-   * Only queue a draw if a line that is drawn relies on the the style context
-   * of the widget (as opposed to a style set manually).
-   */
-  for (guint i = 0; i < priv->lines->len; i++)
-    {
-      const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, i);
-
-      if (line_info->use_default_style)
-        {
-          sp_line_visualizer_row_begin_offscreen_draw (self);
-          break;
-        }
-    }
-}
-
-static void
-sp_line_visualizer_row_set_time_range (SpVisualizerRow *row,
-                                       gint64           begin_time,
-                                       gint64           end_time)
-{
-  SpLineVisualizerRow *self = (SpLineVisualizerRow *)row;
-  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
-
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (row));
-  g_assert (begin_time <= end_time);
-
-  priv->begin_time = begin_time;
-  priv->end_time = end_time;
-
-  sp_line_visualizer_row_queue_reload (self);
-}
-
-static void
-sp_line_visualizer_row_destroy (GtkWidget *widget)
-{
-  SpLineVisualizerRow *self = (SpLineVisualizerRow *)widget;
-  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
-
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
-
-  g_clear_pointer (&priv->surface, cairo_surface_destroy);
-
-  GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->destroy (widget);
-}
-
-static void
 sp_line_visualizer_row_get_property (GObject    *object,
                                      guint       prop_id,
                                      GValue     *value,
@@ -571,12 +384,8 @@ sp_line_visualizer_row_class_init (SpLineVisualizerRowClass *klass)
   object_class->set_property = sp_line_visualizer_row_set_property;
 
   widget_class->draw = sp_line_visualizer_row_draw;
-  widget_class->destroy = sp_line_visualizer_row_destroy;
-  widget_class->size_allocate = sp_line_visualizer_row_size_allocate;
-  widget_class->style_updated = sp_line_visualizer_row_style_updated;
 
   visualizer_class->set_reader = sp_line_visualizer_row_set_reader;
-  visualizer_class->set_time_range = sp_line_visualizer_row_set_time_range;
 
   properties [PROP_TITLE] =
     g_param_spec_string ("title",
@@ -698,7 +507,7 @@ counter_type (LoadData *load,
   return SP_CAPTURE_COUNTER_DOUBLE;
 }
 
-static gfloat
+static inline gfloat
 calc_x (gint64 lower,
         gint64 upper,
         gint64 value)
@@ -706,7 +515,7 @@ calc_x (gint64 lower,
   return (gdouble)(value - lower) / (gdouble)(upper - lower);
 }
 
-static gfloat
+static inline gfloat
 calc_y_double (gdouble lower,
                gdouble upper,
                gdouble value)
@@ -714,7 +523,7 @@ calc_y_double (gdouble lower,
   return (value - lower) / (upper - lower);
 }
 
-static gfloat
+static inline gfloat
 calc_y_int64 (gint64 lower,
               gint64 upper,
               gint64 value)
@@ -772,7 +581,6 @@ sp_line_visualizer_row_load_data_worker (GTask        *task,
 {
   LoadData *load = task_data;
   g_autoptr(GArray) counter_ids = NULL;
-  SpCaptureCondition *left;
 
   g_assert (G_IS_TASK (task));
   g_assert (SP_IS_LINE_VISUALIZER_ROW (source_object));
@@ -783,36 +591,14 @@ sp_line_visualizer_row_load_data_worker (GTask        *task,
   for (guint i = 0; i < load->lines->len; i++)
     {
       const LineInfo *line_info = &g_array_index (load->lines, LineInfo, i);
-
       g_array_append_val (counter_ids, line_info->id);
     }
 
-#if 0
-  /*
-   * Add a little extra time to the visible range so that we can get any datapoints
-   * that might be overlapping the region a bit. This helps so that we can draw
-   * appropriate data points that need a proper x,y coordinate outside the
-   * visible area for cairo_curve_to().
-   *
-   * XXX: This is currently disabled because at certain zoom levels, you will end
-   * up missing data points. We need to always include one datapoint before the
-   * current time range.
-   */
-  SpCaptureCondition *right;
-  gint64 ext_begin_time;
-  gint64 ext_end_time;
-  gint64 ext;
-  ext = (load->end_time - load->begin_time) / 3;
-  ext_begin_time = load->begin_time - ext;
-  ext_end_time = load->end_time + ext;
-  right = sp_capture_condition_new_where_time_between (ext_begin_time, ext_end_time);
-#endif
-
-  left = sp_capture_condition_new_where_counter_in (counter_ids->len, (guint *)(gpointer)counter_ids->data);
-  sp_capture_cursor_add_condition (load->cursor, left);
-
+  sp_capture_cursor_add_condition (
+      load->cursor,
+      sp_capture_condition_new_where_counter_in (counter_ids->len,
+                                                 (guint *)(gpointer)counter_ids->data));
   sp_capture_cursor_foreach (load->cursor, sp_line_visualizer_row_load_data_frame_cb, load);
-
   g_task_return_pointer (task, g_steal_pointer (&load->cache), (GDestroyNotify)point_cache_unref);
 }
 
@@ -845,7 +631,8 @@ sp_line_visualizer_row_load_data_async (SpLineVisualizerRow *self,
   load->cache = point_cache_new ();
   load->y_lower = priv->y_lower;
   load->y_upper = priv->y_upper;
-  sp_visualizer_row_get_time_range (SP_VISUALIZER_ROW (self), &load->begin_time, &load->end_time);
+  load->begin_time = sp_capture_reader_get_start_time (priv->reader);
+  load->end_time = sp_capture_reader_get_end_time (priv->reader);
   load->cursor = sp_capture_cursor_new (priv->reader);
   load->lines = copy_array (priv->lines);
 
@@ -870,205 +657,3 @@ sp_line_visualizer_row_load_data_finish (SpLineVisualizerRow  *self,
 
   return g_task_propagate_pointer (G_TASK (result), error);
 }
-
-static void
-sp_line_visualizer_row_render_worker (GTask        *task,
-                                      gpointer      source_object,
-                                      gpointer      task_data,
-                                      GCancellable *cancellable)
-{
-  RenderData *render = task_data;
-  cairo_surface_t *surface = NULL;
-  cairo_t *cr = NULL;
-
-  g_assert (G_IS_TASK (task));
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (source_object));
-  g_assert (render != NULL);
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  /*
-   * Create our image surface. We do this in the local thread and use
-   * an image surface so that we can work around the lack of thread
-   * safety in cairo and pixman. They are mostly thread unaware though,
-   * so as long as we are really careful, we are okay.
-   *
-   * We draw using ARGB32 because the GtkListBoxRow will be doing it's
-   * own pixel caching. This means we can concentrate on the line data
-   * and ignore having to deal with backgrounds and such.
-   */
-  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                        render->width * render->scale_factor,
-                                        render->height * render->scale_factor);
-
-  if (surface == NULL)
-    {
-      g_task_return_new_error (task,
-                               G_IO_ERROR,
-                               G_IO_ERROR_FAILED,
-                               "Failed to create image surface of size %dx%d",
-                               render->width, render->height);
-      goto cleanup;
-    }
-
-  if (render->scale_factor != 1)
-    cairo_surface_set_device_scale (surface,
-                                    render->scale_factor,
-                                    render->scale_factor);
-
-  cr = cairo_create (surface);
-
-  if (cr == NULL)
-    {
-      g_task_return_new_error (task,
-                               G_IO_ERROR,
-                               G_IO_ERROR_FAILED,
-                               "Failed to create cairo context");
-      goto cleanup;
-    }
-
-  /* Flip the coordinate space so that 0,0 is in the bottom left. */
-  cairo_translate (cr, 0, render->height);
-  cairo_scale (cr, 1.0, -1.0);
-
-  for (guint i = 0; i < render->lines->len; i++)
-    {
-      const LineInfo *line_info = &g_array_index (render->lines, LineInfo, i);
-      const Point *points;
-      guint n_points;
-
-      points = point_cache_get_points (render->cache, line_info->id, &n_points);
-
-      if (n_points > 0)
-        {
-          gdouble last_x = points[0].x * render->width;
-          gdouble last_y = points[0].y * render->height;
-
-          cairo_move_to (cr, last_x, last_y);
-
-          for (guint j = 1; j < n_points; j++)
-            {
-              gdouble x = points[j].x * render->width;
-              gdouble y = points[j].y * render->height;
-
-              cairo_curve_to (cr,
-                              last_x + ((x - last_x) / 2),
-                              last_y,
-                              last_x + ((x - last_x) / 2),
-                              y,
-                              x,
-                              y);
-
-              last_x = x;
-              last_y = y;
-            }
-
-          cairo_set_line_width (cr, line_info->line_width);
-
-          if (line_info->use_default_style)
-            gdk_cairo_set_source_rgba (cr, &render->color);
-          else
-            gdk_cairo_set_source_rgba (cr, &line_info->foreground);
-
-          cairo_stroke (cr);
-        }
-    }
-
-  g_task_return_pointer (task,
-                         g_steal_pointer (&surface),
-                         (GDestroyNotify)cairo_surface_destroy);
-
-cleanup:
-  if (surface != NULL)
-    cairo_surface_destroy (surface);
-
-  if (cr != NULL)
-    cairo_destroy (cr);
-}
-
-/**
- * sp_line_visualizer_row_render_async:
- * @self: an #SpLineVisualizerRow
- * @cache: (transfer full): A #PointCache with points to render
- * @lines: (transfer full): information about the lines
- * @n_lines: number of lines to render
- * @cancellable: (nullable): A #GCancellable or %NULL
- * @callback: a callback for completion
- * @user_data: user data for @callback
- *
- * This asynchronously renders the visuzlier to an offscreen
- * cairo_image_surface_t which cna then be rendered to the front
- * buffer as necessary.
- *
- * Call sp_line_visualizer_row_render_finish() to get the rendered
- * image surface.
- */
-static void
-sp_line_visualizer_row_render_async (SpLineVisualizerRow *self,
-                                     PointCache          *cache,
-                                     GCancellable        *cancellable,
-                                     GAsyncReadyCallback  callback,
-                                     gpointer             user_data)
-{
-  SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self);
-  RenderData *render;
-  g_autoptr(GTask) task = NULL;
-  GtkStyleContext *style_context;
-  GtkAllocation alloc;
-  GtkStateFlags state;
-
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
-  g_assert (cache != NULL);
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-  adjust_alloc_for_borders (self, &alloc);
-
-  task = g_task_new (self, cancellable, callback, user_data);
-  g_task_set_source_tag (task, sp_line_visualizer_row_render_async);
-
-  render = g_slice_new0 (RenderData);
-  render->cache = point_cache_ref (cache);
-  render->lines = copy_array (priv->lines);
-  render->width = alloc.width;
-  render->height = alloc.height;
-  render->scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
-
-  sp_visualizer_row_get_time_range (SP_VISUALIZER_ROW (self), &render->begin_time, NULL);
-
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  state = gtk_widget_get_state_flags (GTK_WIDGET (self));
-  gtk_style_context_get_color (style_context, state, &render->color);
-
-  g_task_set_task_data (task, render, render_data_free);
-  g_task_run_in_thread (task, sp_line_visualizer_row_render_worker);
-}
-
-/**
- * sp_line_visualizer_row_render_finish:
- *
- * This completes an asynchronous request to render the visualizer.
- * The resulting cairo_surface_t will really be a cairo_image_surface_t.
- * We do not use a "similar surface" because we need to ensure that
- * the render can be performed in a thread-safe manner as it is
- * dispatched to another thread.
- *
- * Returns: (transfer full): A #cairo_image_surface_t.
- */
-static cairo_surface_t *
-sp_line_visualizer_row_render_finish (SpLineVisualizerRow  *self,
-                                      GAsyncResult         *result,
-                                      gint64               *surface_begin_time,
-                                      GError              **error)
-{
-  RenderData *render;
-
-  g_assert (SP_IS_LINE_VISUALIZER_ROW (self));
-  g_assert (G_IS_TASK (result));
-
-  render = g_task_get_task_data (G_TASK (result));
-
-  if (surface_begin_time != NULL)
-    *surface_begin_time = render->begin_time;
-
-  return g_task_propagate_pointer (G_TASK (result), error);
-}
diff --git a/lib/sp-visualizer-list.c b/lib/sp-visualizer-list.c
index 0287100..0ea799f 100644
--- a/lib/sp-visualizer-list.c
+++ b/lib/sp-visualizer-list.c
@@ -22,12 +22,15 @@
 
 #include "sp-visualizer-list.h"
 #include "sp-visualizer-row.h"
+#include "sp-zoom-manager.h"
 
-#define NSEC_PER_SEC G_GUINT64_CONSTANT(1000000000)
+#define NSEC_PER_SEC              G_GUINT64_CONSTANT(1000000000)
+#define DEFAULT_PIXELS_PER_SECOND 20
 
 typedef struct
 {
   SpCaptureReader *reader;
+  SpZoomManager *zoom_manager;
   gint64 begin_time;
   gint64 end_time;
 } SpVisualizerListPrivate;
@@ -35,6 +38,7 @@ typedef struct
 enum {
   PROP_0,
   PROP_READER,
+  PROP_ZOOM_MANAGER,
   N_PROPS
 };
 
@@ -43,6 +47,22 @@ G_DEFINE_TYPE_WITH_PRIVATE (SpVisualizerList, sp_visualizer_list, GTK_TYPE_LIST_
 static GParamSpec *properties [N_PROPS];
 
 static void
+sp_visualizer_list_add (GtkContainer *container,
+                        GtkWidget    *widget)
+{
+  SpVisualizerList *self = (SpVisualizerList *)container;
+  SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
+
+  GTK_CONTAINER_CLASS (sp_visualizer_list_parent_class)->add (container, widget);
+
+  if (SP_IS_VISUALIZER_ROW (widget))
+    {
+      sp_visualizer_row_set_reader (SP_VISUALIZER_ROW (widget), priv->reader);
+      sp_visualizer_row_set_zoom_manager (SP_VISUALIZER_ROW (widget), priv->zoom_manager);
+    }
+}
+
+static void
 sp_visualizer_list_finalize (GObject *object)
 {
   SpVisualizerList *self = (SpVisualizerList *)object;
@@ -67,6 +87,10 @@ sp_visualizer_list_get_property (GObject    *object,
       g_value_set_boxed (value, sp_visualizer_list_get_reader (self));
       break;
 
+    case PROP_ZOOM_MANAGER:
+      g_value_set_object (value, sp_visualizer_list_get_zoom_manager (self));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -86,6 +110,10 @@ sp_visualizer_list_set_property (GObject      *object,
       sp_visualizer_list_set_reader (self, g_value_get_boxed (value));
       break;
 
+    case PROP_ZOOM_MANAGER:
+      sp_visualizer_list_set_zoom_manager (self, g_value_get_object (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -95,11 +123,14 @@ static void
 sp_visualizer_list_class_init (SpVisualizerListClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
   object_class->finalize = sp_visualizer_list_finalize;
   object_class->get_property = sp_visualizer_list_get_property;
   object_class->set_property = sp_visualizer_list_set_property;
 
+  container_class->add = sp_visualizer_list_add;
+
   properties [PROP_READER] =
     g_param_spec_boxed ("reader",
                         "Reader",
@@ -107,6 +138,13 @@ sp_visualizer_list_class_init (SpVisualizerListClass *klass)
                         SP_TYPE_CAPTURE_READER,
                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_ZOOM_MANAGER] =
+    g_param_spec_object ("zoom-manager",
+                         "Zoom Manager",
+                         "The zoom manager",
+                         SP_TYPE_ZOOM_MANAGER,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
@@ -121,50 +159,6 @@ sp_visualizer_list_new (void)
   return g_object_new (SP_TYPE_VISUALIZER_ROW, NULL);
 }
 
-static void
-propagate_time (GtkWidget *widget,
-                gpointer   user_data)
-{
-  struct {
-    gint64 begin;
-    gint64 end;
-  } *t = user_data;
-
-  if (SP_IS_VISUALIZER_ROW (widget))
-    sp_visualizer_row_set_time_range (SP_VISUALIZER_ROW (widget), t->begin, t->end);
-}
-
-void
-sp_visualizer_list_set_time_range (SpVisualizerList *self,
-                                   gint64            begin_time,
-                                   gint64            end_time)
-{
-  struct {
-    gint64 begin;
-    gint64 end;
-  } t = { begin_time, end_time };
-
-  g_return_if_fail (SP_IS_VISUALIZER_LIST (self));
-
-  gtk_container_foreach (GTK_CONTAINER (self), propagate_time, &t);
-}
-
-void
-sp_visualizer_list_get_time_range (SpVisualizerList *self,
-                                   gint64           *begin_time,
-                                   gint64           *end_time)
-{
-  SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
-
-  g_return_if_fail (SP_IS_VISUALIZER_LIST (self));
-
-  if (begin_time)
-    *begin_time = priv->begin_time;
-
-  if (end_time)
-    *end_time = priv->end_time;
-}
-
 /**
  * sp_visualizer_list_get_reader:
  *
@@ -182,49 +176,58 @@ sp_visualizer_list_get_reader (SpVisualizerList *self)
   return priv->reader;
 }
 
-static void
-propagate_reader (GtkWidget *widget,
-                  gpointer   user_data)
-{
-  SpCaptureReader *reader = user_data;
-
-  if (SP_IS_VISUALIZER_ROW (widget))
-    sp_visualizer_row_set_reader (SP_VISUALIZER_ROW (widget), reader);
-}
-
-static void
-sp_visualizer_list_update_time_range (SpVisualizerList *self)
+void
+sp_visualizer_list_set_reader (SpVisualizerList *self,
+                               SpCaptureReader  *reader)
 {
   SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
-  gint64 begin_time = 0;
-  gint64 end_time = 0;
 
   g_return_if_fail (SP_IS_VISUALIZER_LIST (self));
 
-  if (priv->reader != NULL)
+  if (reader != priv->reader)
     {
-      begin_time = sp_capture_reader_get_start_time (priv->reader);
-      end_time = begin_time + (NSEC_PER_SEC * 60);
-    }
+      g_clear_pointer (&priv->reader, sp_capture_reader_unref);
+
+      if (reader != NULL)
+        priv->reader = sp_capture_reader_ref (reader);
+
+      gtk_container_foreach (GTK_CONTAINER (self),
+                             (GtkCallback)sp_visualizer_row_set_reader,
+                             reader);
 
-  sp_visualizer_list_set_time_range (self, begin_time, end_time);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READER]);
+    }
 }
 
 void
-sp_visualizer_list_set_reader (SpVisualizerList *self,
-                               SpCaptureReader  *reader)
+sp_visualizer_list_set_zoom_manager (SpVisualizerList *self,
+                                     SpZoomManager    *zoom_manager)
 {
   SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
 
   g_return_if_fail (SP_IS_VISUALIZER_LIST (self));
+  g_return_if_fail (SP_IS_ZOOM_MANAGER (zoom_manager));
 
-  if (reader != priv->reader)
+  if (g_set_object (&priv->zoom_manager, zoom_manager))
     {
-      g_clear_pointer (&priv->reader, sp_capture_reader_unref);
-      if (reader)
-        priv->reader = sp_capture_reader_ref (reader);
-      gtk_container_foreach (GTK_CONTAINER (self), propagate_reader, reader);
-      sp_visualizer_list_update_time_range (self);
-      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READER]);
+      gtk_container_foreach (GTK_CONTAINER (self),
+                             (GtkCallback)sp_visualizer_row_set_zoom_manager,
+                             zoom_manager);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM_MANAGER]);
     }
 }
+
+/**
+ * sp_visualizer_list_get_zoom_manager:
+ *
+ * Returns: (nullable) (transfer): A #SpZoomManager or %NULL.
+ */
+SpZoomManager *
+sp_visualizer_list_get_zoom_manager (SpVisualizerList *self)
+{
+  SpVisualizerListPrivate *priv = sp_visualizer_list_get_instance_private (self);
+
+  g_return_val_if_fail (SP_IS_VISUALIZER_LIST (self), NULL);
+
+  return priv->zoom_manager;
+}
diff --git a/lib/sp-visualizer-list.h b/lib/sp-visualizer-list.h
index f288980..70ec415 100644
--- a/lib/sp-visualizer-list.h
+++ b/lib/sp-visualizer-list.h
@@ -22,6 +22,7 @@
 #include <gtk/gtk.h>
 
 #include "sp-capture-reader.h"
+#include "sp-zoom-manager.h"
 
 G_BEGIN_DECLS
 
@@ -43,16 +44,13 @@ struct _SpVisualizerListClass
   gpointer _reserved8;
 };
 
-GtkWidget       *sp_visualizer_list_new            (void);
-void             sp_visualizer_list_set_reader     (SpVisualizerList *self,
-                                                    SpCaptureReader  *reader);
-SpCaptureReader *sp_visualizer_list_get_reader     (SpVisualizerList *self);
-void             sp_visualizer_list_set_time_range (SpVisualizerList *self,
-                                                    gint64            begin_time,
-                                                    gint64            end_time);
-void             sp_visualizer_list_get_time_range (SpVisualizerList *self,
-                                                    gint64           *begin_time,
-                                                    gint64           *end_time);
+GtkWidget       *sp_visualizer_list_new              (void);
+void             sp_visualizer_list_set_reader       (SpVisualizerList *self,
+                                                      SpCaptureReader  *reader);
+SpCaptureReader *sp_visualizer_list_get_reader       (SpVisualizerList *self);
+SpZoomManager   *sp_visualizer_list_get_zoom_manager (SpVisualizerList *self);
+void             sp_visualizer_list_set_zoom_manager (SpVisualizerList *self,
+                                                      SpZoomManager    *zoom_manager);
 
 G_END_DECLS
 
diff --git a/lib/sp-visualizer-row.c b/lib/sp-visualizer-row.c
index 550c102..35f59f1 100644
--- a/lib/sp-visualizer-row.c
+++ b/lib/sp-visualizer-row.c
@@ -16,19 +16,143 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define G_LOG_DOMAIN "sp-visualizer-row"
+
 #include "sp-visualizer-row.h"
 
+#define NSEC_PER_SEC              G_GINT64_CONSTANT(1000000000)
+#define DEFAULT_PIXELS_PER_SECOND 20
+
 typedef struct
 {
-  gint64 begin_time;
-  gint64 end_time;
+  SpCaptureReader *reader;
+  SpZoomManager   *zoom_manager;
 } SpVisualizerRowPrivate;
 
+enum {
+  PROP_0,
+  PROP_ZOOM_MANAGER,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SpVisualizerRow, sp_visualizer_row, GTK_TYPE_LIST_BOX_ROW)
 
+static gint
+sp_visualizer_row_get_graph_width (SpVisualizerRow *self)
+{
+  SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
+  gdouble zoom_level = 1.0;
+  gint64 begin_time;
+  gint64 end_time;
+
+  g_assert (SP_IS_VISUALIZER_ROW (self));
+
+  if (priv->reader == NULL)
+    return 0;
+
+  if (priv->zoom_manager != NULL)
+    zoom_level = sp_zoom_manager_get_zoom (priv->zoom_manager);
+
+  begin_time = sp_capture_reader_get_start_time (priv->reader);
+  end_time = sp_capture_reader_get_end_time (priv->reader);
+
+  return (end_time - begin_time)
+         / (gdouble)NSEC_PER_SEC
+         * zoom_level
+         * DEFAULT_PIXELS_PER_SECOND;
+}
+
+static void
+sp_visualizer_row_get_preferred_width (GtkWidget *widget,
+                                       gint      *min_width,
+                                       gint      *nat_width)
+{
+  SpVisualizerRow *self = (SpVisualizerRow *)widget;
+  gint graph_width;
+  gint real_min_width = 0;
+  gint real_nat_width = 0;
+
+  g_assert (SP_IS_VISUALIZER_ROW (self));
+
+  GTK_WIDGET_CLASS (sp_visualizer_row_parent_class)->get_preferred_width (widget, &real_min_width, 
&real_nat_width);
+
+  graph_width = sp_visualizer_row_get_graph_width (self);
+
+  *min_width = *nat_width = real_min_width + graph_width;
+}
+
+static void
+sp_visualizer_row_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  SpVisualizerRow *self = SP_VISUALIZER_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_ZOOM_MANAGER:
+      g_value_set_object (value, sp_visualizer_row_get_zoom_manager (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sp_visualizer_row_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  SpVisualizerRow *self = SP_VISUALIZER_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_ZOOM_MANAGER:
+      sp_visualizer_row_set_zoom_manager (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sp_visualizer_row_finalize (GObject *object)
+{
+  SpVisualizerRow *self = (SpVisualizerRow *)object;
+  SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
+
+  g_clear_pointer (&priv->reader, sp_capture_reader_unref);
+  g_clear_object (&priv->zoom_manager);
+
+  G_OBJECT_CLASS (sp_visualizer_row_parent_class)->finalize (object);
+}
+
 static void
 sp_visualizer_row_class_init (SpVisualizerRowClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = sp_visualizer_row_finalize;
+  object_class->get_property = sp_visualizer_row_get_property;
+  object_class->set_property = sp_visualizer_row_set_property;
+
+  widget_class->get_preferred_width = sp_visualizer_row_get_preferred_width;
+
+  properties [PROP_ZOOM_MANAGER] =
+    g_param_spec_object ("zoom-manager",
+                         "Zoom Manager",
+                         "Zoom Manager",
+                         SP_TYPE_ZOOM_MANAGER,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
 static void
@@ -36,53 +160,144 @@ sp_visualizer_row_init (SpVisualizerRow *self)
 {
 }
 
+static void
+sp_visualizer_row_zoom_manager_notify_zoom (SpVisualizerRow *self,
+                                            GParamSpec      *pspec,
+                                            SpZoomManager   *zoom_manager)
+{
+  g_assert (SP_IS_VISUALIZER_ROW (self));
+  g_assert (SP_IS_ZOOM_MANAGER (zoom_manager));
+
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+/**
+ * sp_visualizer_row_get_zoom_manager:
+ *
+ * Returns: (transfer none) (nullable): A #SpZoomManager or %NULL.
+ */
+SpZoomManager *
+sp_visualizer_row_get_zoom_manager (SpVisualizerRow *self)
+{
+  SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
+
+  g_return_val_if_fail (SP_IS_VISUALIZER_ROW (self), NULL);
+
+  return priv->zoom_manager;
+}
+
 void
-sp_visualizer_row_set_reader (SpVisualizerRow *self,
-                              SpCaptureReader *reader)
+sp_visualizer_row_set_zoom_manager (SpVisualizerRow *self,
+                                    SpZoomManager   *zoom_manager)
 {
+  SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
+
   g_return_if_fail (SP_IS_VISUALIZER_ROW (self));
+  g_return_if_fail (!zoom_manager || SP_IS_ZOOM_MANAGER (zoom_manager));
 
-  if (SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader)
-    SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader (self, reader);
+  if (priv->zoom_manager != zoom_manager)
+    {
+      if (priv->zoom_manager != NULL)
+        {
+          g_signal_handlers_disconnect_by_func (priv->zoom_manager,
+                                                G_CALLBACK (sp_visualizer_row_zoom_manager_notify_zoom),
+                                                self);
+          g_clear_object (&priv->zoom_manager);
+        }
+
+      if (zoom_manager != NULL)
+        {
+          priv->zoom_manager = g_object_ref (zoom_manager);
+          g_signal_connect_object (priv->zoom_manager,
+                                   "notify::zoom",
+                                   G_CALLBACK (sp_visualizer_row_zoom_manager_notify_zoom),
+                                   self,
+                                   G_CONNECT_SWAPPED);
+        }
+
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM_MANAGER]);
+      gtk_widget_queue_resize (GTK_WIDGET (self));
+    }
 }
 
 void
-sp_visualizer_row_set_time_range (SpVisualizerRow *self,
-                                  gint64           begin_time,
-                                  gint64           end_time)
+sp_visualizer_row_set_reader (SpVisualizerRow *self,
+                              SpCaptureReader *reader)
 {
   SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
 
   g_return_if_fail (SP_IS_VISUALIZER_ROW (self));
 
-  if (begin_time > end_time)
+  if (priv->reader != reader)
     {
-      gint64 tmp = begin_time;
-      begin_time = end_time;
-      end_time = tmp;
+      g_clear_pointer (&priv->reader, sp_capture_reader_unref);
+
+      if (reader != NULL)
+        priv->reader = sp_capture_reader_ref (reader);
+
+      if (SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader)
+        SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader (self, reader);
+
+      gtk_widget_queue_resize (GTK_WIDGET (self));
     }
+}
+
+static inline void
+subtract_border (GtkAllocation *alloc,
+                 GtkBorder     *border)
+{
+#if 0
+  g_print ("Border; %d %d %d %d\n", border->top, border->left, border->bottom, border->right);
+#endif
+
+  alloc->x += border->left;
+  alloc->y += border->top;
+  alloc->width -= border->left + border->right;
+  alloc->height -= border->top + border->bottom;
+}
+
+static void
+adjust_alloc_for_borders (SpVisualizerRow *self,
+                          GtkAllocation   *alloc)
+{
+  GtkStyleContext *style_context;
+  GtkBorder border;
+  GtkStateFlags state;
 
-  priv->begin_time = begin_time;
-  priv->end_time = end_time;
+  g_assert (SP_IS_VISUALIZER_ROW (self));
+  g_assert (alloc != NULL);
 
-  if (SP_VISUALIZER_ROW_GET_CLASS (self)->set_time_range)
-    SP_VISUALIZER_ROW_GET_CLASS (self)->set_time_range (self, begin_time, end_time);
+  state = gtk_widget_get_state_flags (GTK_WIDGET (self));
+  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+  gtk_style_context_get_border (style_context, state, &border);
 
-  gtk_widget_queue_draw (GTK_WIDGET (self));
+  subtract_border (alloc, &border);
 }
 
 void
-sp_visualizer_row_get_time_range (SpVisualizerRow *self,
-                                  gint64          *begin_time,
-                                  gint64          *end_time)
+sp_visualizer_row_translate_points (SpVisualizerRow                    *self,
+                                    const SpVisualizerRowRelativePoint *in_points,
+                                    guint                               n_in_points,
+                                    SpVisualizerRowAbsolutePoint       *out_points,
+                                    guint                               n_out_points)
 {
-  SpVisualizerRowPrivate *priv = sp_visualizer_row_get_instance_private (self);
+  GtkAllocation alloc;
+  gint graph_width;
 
   g_return_if_fail (SP_IS_VISUALIZER_ROW (self));
+  g_return_if_fail (in_points != NULL);
+  g_return_if_fail (out_points != NULL);
+  g_return_if_fail (n_in_points == n_out_points);
+
+  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+  adjust_alloc_for_borders (self, &alloc);
 
-  if (begin_time != NULL)
-    *begin_time = priv->begin_time;
+  graph_width = sp_visualizer_row_get_graph_width (self);
 
-  if (end_time != NULL)
-    *end_time = priv->end_time;
+  for (guint i = 0; i < n_in_points; i++)
+    {
+      out_points[i].x = alloc.x + (in_points[i].x * graph_width);
+      out_points[i].y = alloc.y + alloc.height - (in_points[i].y * alloc.height);
+    }
 }
+
diff --git a/lib/sp-visualizer-row.h b/lib/sp-visualizer-row.h
index 49c828f..aa67847 100644
--- a/lib/sp-visualizer-row.h
+++ b/lib/sp-visualizer-row.h
@@ -22,6 +22,7 @@
 #include <gtk/gtk.h>
 
 #include "sp-capture-reader.h"
+#include "sp-zoom-manager.h"
 
 G_BEGIN_DECLS
 
@@ -29,6 +30,18 @@ G_BEGIN_DECLS
 
 G_DECLARE_DERIVABLE_TYPE (SpVisualizerRow, sp_visualizer_row, SP, VISUALIZER_ROW, GtkListBoxRow)
 
+typedef struct
+{
+  gfloat x;
+  gfloat y;
+} SpVisualizerRowRelativePoint;
+
+typedef struct
+{
+  gint x;
+  gint y;
+} SpVisualizerRowAbsolutePoint;
+
 struct _SpVisualizerRowClass
 {
   GtkListBoxRowClass parent_class;
@@ -43,21 +56,6 @@ struct _SpVisualizerRowClass
   void (*set_reader) (SpVisualizerRow *self,
                       SpCaptureReader *reader);
 
-  /**
-   * SpVisualizerRow::set_time_range:
-   * @self: A #SpVisualizerRow
-   * @begin_time: the beginning time for the row.
-   * @end_time: the end time for the row.
-   *
-   * This function is used to notify the row that the range the
-   * row should be displaying has changed. This might happen when
-   * the user has altered the zoom level, selected a region, in or
-   * a new capture was loaded.
-   */
-  void (*set_time_range) (SpVisualizerRow *self,
-                          gint64           begin_time,
-                          gint64           end_time);
-
   gpointer _reserved1;
   gpointer _reserved2;
   gpointer _reserved3;
@@ -76,14 +74,16 @@ struct _SpVisualizerRowClass
   gpointer _reserved16;
 };
 
-void sp_visualizer_row_set_reader     (SpVisualizerRow *self,
-                                       SpCaptureReader *reader);
-void sp_visualizer_row_get_time_range (SpVisualizerRow *self,
-                                       gint64          *begin_time,
-                                       gint64          *end_time);
-void sp_visualizer_row_set_time_range (SpVisualizerRow *self,
-                                       gint64           begin_time,
-                                       gint64           end_time);
+void           sp_visualizer_row_set_reader       (SpVisualizerRow *self,
+                                                   SpCaptureReader *reader);
+SpZoomManager *sp_visualizer_row_get_zoom_manager (SpVisualizerRow *self);
+void           sp_visualizer_row_set_zoom_manager (SpVisualizerRow *self,
+                                                   SpZoomManager   *zoom_manager);
+void           sp_visualizer_row_translate_points (SpVisualizerRow                    *self,
+                                                   const SpVisualizerRowRelativePoint *in_points,
+                                                   guint                               n_in_points,
+                                                   SpVisualizerRowAbsolutePoint       *out_points,
+                                                   guint                               n_out_points);
 
 G_END_DECLS
 
diff --git a/lib/sp-visualizer-view.c b/lib/sp-visualizer-view.c
index c8a4b69..5890ed5 100644
--- a/lib/sp-visualizer-view.c
+++ b/lib/sp-visualizer-view.c
@@ -86,77 +86,6 @@ sp_visualizer_view_row_removed (SpVisualizerView *self,
 }
 
 static void
-sp_visualizer_view_set_time_range (SpVisualizerView *self,
-                                   gint64            begin_time,
-                                   gint64            end_time)
-{
-  SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
-
-  g_assert (SP_IS_VISUALIZER_VIEW (self));
-
-  if (end_time < begin_time)
-    {
-      gint64 tmp = begin_time;
-      begin_time = end_time;
-      end_time = tmp;
-    }
-
-  sp_visualizer_list_set_time_range (priv->list, begin_time, end_time);
-  sp_visualizer_ticks_set_time_range (priv->ticks, begin_time, end_time);
-}
-
-static void
-sp_visualizer_view_adjustment_value_changed (SpVisualizerView *self,
-                                             GtkAdjustment    *adjustment)
-{
-  gint64 begin_time;
-  gint64 end_time;
-
-  g_assert (SP_IS_VISUALIZER_VIEW (self));
-  g_assert (GTK_IS_ADJUSTMENT (adjustment));
-
-  begin_time = gtk_adjustment_get_value (adjustment);
-  end_time = begin_time + gtk_adjustment_get_page_size (adjustment);
-  sp_visualizer_view_set_time_range (self, begin_time, end_time);
-}
-
-static void
-sp_visualizer_view_notify_zoom (SpVisualizerView *self,
-                                GParamSpec       *pspec,
-                                SpZoomManager    *zoom_manager)
-{
-  SpVisualizerViewPrivate *priv = sp_visualizer_view_get_instance_private (self);
-  gint64 begin_time = 0.0;
-  gint64 end_time;
-  gdouble zoom;
-  gdouble page_size;
-  gdouble upper;
-  gdouble value;
-
-  g_assert (SP_IS_VISUALIZER_VIEW (self));
-  g_assert (SP_IS_ZOOM_MANAGER (zoom_manager));
-
-  zoom = sp_zoom_manager_get_zoom (zoom_manager);
-
-  if (priv->reader != NULL)
-    begin_time = sp_capture_reader_get_start_time (priv->reader);
-
-  end_time = begin_time + (NSEC_PER_SEC * 60.0 / zoom);
-
-  sp_visualizer_view_set_time_range (self, begin_time, end_time);
-  gtk_adjustment_set_page_size (priv->scroll_adjustment, end_time - begin_time);
-
-  g_object_get (priv->scroll_adjustment,
-                "page-size", &page_size,
-                "upper", &upper,
-                "value", &value,
-                NULL);
-
-  if (value + page_size > upper)
-    gtk_adjustment_set_value (priv->scroll_adjustment, upper - page_size);
-}
-
-static void
 sp_visualizer_view_finalize (GObject *object)
 {
   SpVisualizerView *self = (SpVisualizerView *)object;
@@ -282,12 +211,6 @@ sp_visualizer_view_init (SpVisualizerView *self)
                            G_CALLBACK (sp_visualizer_view_row_removed),
                            self,
                            G_CONNECT_SWAPPED);
-
-  g_signal_connect_object (priv->scroll_adjustment,
-                           "value-changed",
-                           G_CALLBACK (sp_visualizer_view_adjustment_value_changed),
-                           self,
-                           G_CONNECT_SWAPPED);
 }
 
 /**
@@ -316,28 +239,10 @@ sp_visualizer_view_set_reader (SpVisualizerView *self,
   if (priv->reader != reader)
     {
       g_clear_pointer (&priv->reader, sp_capture_reader_unref);
-      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READER]);
-      sp_visualizer_list_set_reader (priv->list, reader);
       if (reader != NULL)
-        {
-          gint64 begin_time = 0;
-          gint64 end_time = 0;
-
-          priv->reader = sp_capture_reader_ref (reader);
-          begin_time = sp_capture_reader_get_start_time (reader);
-          end_time = sp_capture_reader_get_end_time (reader);
-
-          sp_visualizer_ticks_set_epoch (priv->ticks, begin_time);
-
-          g_object_set (priv->scroll_adjustment,
-                        "lower", (gdouble)begin_time,
-                        "upper", (gdouble)end_time,
-                        "value", (gdouble)begin_time,
-                        NULL);
-        }
-
-      if (priv->zoom_manager != NULL)
-        sp_visualizer_view_notify_zoom (self, NULL, priv->zoom_manager);
+        priv->reader = sp_capture_reader_ref (reader);
+      sp_visualizer_list_set_reader (priv->list, reader);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READER]);
     }
 }
 
@@ -370,6 +275,11 @@ buildable_iface_init (GtkBuildableIface *iface)
   iface->add_child = sp_visualizer_view_add_child;
 }
 
+/**
+ * sp_visualizer_view_get_zoom_manager:
+ *
+ * Returns: (transfer none) (nullable): An #SpZoomManager or %NULL
+ */
 SpZoomManager *
 sp_visualizer_view_get_zoom_manager (SpVisualizerView *self)
 {
@@ -389,27 +299,9 @@ sp_visualizer_view_set_zoom_manager (SpVisualizerView *self,
   g_return_if_fail (SP_IS_VISUALIZER_VIEW (self));
   g_return_if_fail (!zoom_manager || SP_IS_ZOOM_MANAGER (zoom_manager));
 
-  if (zoom_manager != priv->zoom_manager)
+  if (g_set_object (&priv->zoom_manager, zoom_manager))
     {
-      if (priv->zoom_manager != NULL)
-        {
-          g_signal_handlers_disconnect_by_func (priv->zoom_manager,
-                                                G_CALLBACK (sp_visualizer_view_notify_zoom),
-                                                self);
-          g_clear_object (&priv->zoom_manager);
-        }
-
-      if (zoom_manager != NULL)
-        {
-          priv->zoom_manager = g_object_ref (zoom_manager);
-          g_signal_connect_object (priv->zoom_manager,
-                                   "notify::zoom",
-                                   G_CALLBACK (sp_visualizer_view_notify_zoom),
-                                   self,
-                                   G_CONNECT_SWAPPED);
-          sp_visualizer_view_notify_zoom (self, NULL, priv->zoom_manager);
-        }
-
+      sp_visualizer_list_set_zoom_manager (priv->list, zoom_manager);
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM_MANAGER]);
       gtk_widget_queue_resize (GTK_WIDGET (self));
     }


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