diff --git a/lib/resources/ui/sp-visualizer-view.ui b/lib/resources/ui/sp-visualizer-view.ui index 9970393e..45ec5226 100644 --- a/lib/resources/ui/sp-visualizer-view.ui +++ b/lib/resources/ui/sp-visualizer-view.ui @@ -12,26 +12,13 @@ - - vertical + + true + false true - - scroll_adjustment - true - true + true - - - true - scroll_adjustment - - - true - - - - diff --git a/lib/sp-line-visualizer-row.c b/lib/sp-line-visualizer-row.c index 5153de32..0a92e5ac 100644 --- a/lib/sp-line-visualizer-row.c +++ b/lib/sp-line-visualizer-row.c @@ -46,13 +46,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. */ @@ -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 @@ -85,17 +74,6 @@ typedef struct guint use_default_style : 1; } LineInfo; -typedef struct -{ - PointCache *cache; - GArray *lines; - GdkRGBA color; - gint64 begin_time; - gint scale_factor; - gint width; - gint height; -} RenderData; - typedef struct { SpCaptureCursor *cursor; @@ -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) -{ - SpLineVisualizerRow *self = (SpLineVisualizerRow *)object; - 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); - - priv->surface = surface; - priv->surface_begin_time = surface_begin_time; - - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -static void -sp_line_visualizer_row_begin_offscreen_draw (SpLineVisualizerRow *self) -{ - SpLineVisualizerRowPrivate *priv = sp_line_visualizer_row_get_instance_private (self); - - g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); - - if (priv->cache != NULL) - sp_line_visualizer_row_render_async (self, - priv->cache, - NULL, - sp_line_visualizer_row_render_cb, - NULL); -} - -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 (SpLineVisualizerRow *self, - GtkAllocation *alloc) -{ - GtkStyleContext *style_context; - GtkBorder border; - GtkStateFlags state; - - g_assert (SP_IS_LINE_VISUALIZER_ROW (self)); - g_assert (alloc != NULL); - - 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); - - subtract_border (alloc, &border); -} - -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; -} - 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; + GtkStyleContext *style_context; + GtkStateFlags flags; + GdkRGBA foreground; gboolean ret; g_assert (SP_IS_LINE_VISUALIZER_ROW (widget)); g_assert (cr != NULL); - gtk_widget_get_allocation (widget, &alloc); - adjust_alloc_for_borders (self, &alloc); - ret = GTK_WIDGET_CLASS (sp_line_visualizer_row_parent_class)->draw (widget, cr); - if (ret == GDK_EVENT_PROPAGATE && priv->surface != NULL) + if (priv->cache == NULL) + return ret; + + style_context = gtk_widget_get_style_context (widget); + flags = gtk_widget_get_state_flags (widget); + gtk_style_context_get_color (style_context, flags, &foreground); + + for (guint line = 0; line < priv->lines->len; line++) { - gint scale_factor = gtk_widget_get_scale_factor (widget); - gint width; - gint height; - gint x_offset = 0; + g_autofree SpVisualizerRowAbsolutePoint *points = NULL; + const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line); + const Point *fpoints; + guint n_fpoints = 0; - width = cairo_image_surface_get_width (priv->surface) / scale_factor; - height = cairo_image_surface_get_height (priv->surface) / scale_factor; + fpoints = point_cache_get_points (priv->cache, line_info->id, &n_fpoints); - /* - * 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) + if (n_fpoints > 0) { - 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; + gfloat last_x; + gfloat last_y; + + points = g_new0 (SpVisualizerRowAbsolutePoint, n_fpoints); + + sp_visualizer_row_translate_points (SP_VISUALIZER_ROW (self), + (const SpVisualizerRowRelativePoint *)fpoints, + n_fpoints, + points, + n_fpoints); + + last_x = points[0].x; + last_y = points[0].y; + + cairo_move_to (cr, last_x, last_y); + + 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; + } + + cairo_set_line_width (cr, line_info->line_width); + + if (line_info->use_default_style) + gdk_cairo_set_source_rgba (cr, &foreground); + else + gdk_cairo_set_source_rgba (cr, &line_info->foreground); + + 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 @@ -431,76 +314,6 @@ sp_line_visualizer_row_finalize (GObject *object) G_OBJECT_CLASS (sp_line_visualizer_row_parent_class)->finalize (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, @@ -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 0287100d..0ea799fa 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 }; @@ -42,6 +46,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) { @@ -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,34 +176,6 @@ 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) -{ - 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) - { - begin_time = sp_capture_reader_get_start_time (priv->reader); - end_time = begin_time + (NSEC_PER_SEC * 60); - } - - sp_visualizer_list_set_time_range (self, begin_time, end_time); -} - void sp_visualizer_list_set_reader (SpVisualizerList *self, SpCaptureReader *reader) @@ -221,10 +187,47 @@ sp_visualizer_list_set_reader (SpVisualizerList *self, if (reader != priv->reader) { g_clear_pointer (&priv->reader, sp_capture_reader_unref); - if (reader) + + if (reader != NULL) priv->reader = sp_capture_reader_ref (reader); - gtk_container_foreach (GTK_CONTAINER (self), propagate_reader, reader); - sp_visualizer_list_update_time_range (self); + + gtk_container_foreach (GTK_CONTAINER (self), + (GtkCallback)sp_visualizer_row_set_reader, + reader); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READER]); } } + +void +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 (g_set_object (&priv->zoom_manager, zoom_manager)) + { + 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 f2889809..70ec415a 100644 --- a/lib/sp-visualizer-list.h +++ b/lib/sp-visualizer-list.h @@ -22,6 +22,7 @@ #include #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 550c102e..35f59f12 100644 --- a/lib/sp-visualizer-row.c +++ b/lib/sp-visualizer-row.c @@ -16,19 +16,143 @@ * along with this program. If not, see . */ +#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_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 (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_reader (SpVisualizerRow *self, SpCaptureReader *reader) -{ - g_return_if_fail (SP_IS_VISUALIZER_ROW (self)); - - if (SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader) - SP_VISUALIZER_ROW_GET_CLASS (self)->set_reader (self, reader); -} - -void -sp_visualizer_row_set_time_range (SpVisualizerRow *self, - gint64 begin_time, - gint64 end_time) { 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)); } +} - priv->begin_time = begin_time; - priv->end_time = end_time; +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 - if (SP_VISUALIZER_ROW_GET_CLASS (self)->set_time_range) - SP_VISUALIZER_ROW_GET_CLASS (self)->set_time_range (self, begin_time, end_time); + alloc->x += border->left; + alloc->y += border->top; + alloc->width -= border->left + border->right; + alloc->height -= border->top + border->bottom; +} - gtk_widget_queue_draw (GTK_WIDGET (self)); +static void +adjust_alloc_for_borders (SpVisualizerRow *self, + GtkAllocation *alloc) +{ + GtkStyleContext *style_context; + GtkBorder border; + GtkStateFlags state; + + g_assert (SP_IS_VISUALIZER_ROW (self)); + g_assert (alloc != NULL); + + 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); + + 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); - if (begin_time != NULL) - *begin_time = priv->begin_time; + gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); + adjust_alloc_for_borders (self, &alloc); - if (end_time != NULL) - *end_time = priv->end_time; + graph_width = sp_visualizer_row_get_graph_width (self); + + 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 49c828fc..aa67847d 100644 --- a/lib/sp-visualizer-row.h +++ b/lib/sp-visualizer-row.h @@ -22,6 +22,7 @@ #include #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 c8a4b699..5890ed5e 100644 --- a/lib/sp-visualizer-view.c +++ b/lib/sp-visualizer-view.c @@ -85,77 +85,6 @@ sp_visualizer_view_row_removed (SpVisualizerView *self, g_signal_emit (self, signals [VISUALIZER_REMOVED], 0, widget); } -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) { @@ -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)); }