/** * ppg_visualizer_set_is_important: * @visualizer: (in): A #PpgVisualizer. * * Sets if this visualizer is important. Important visualizers can be shown * when an instrument is collaposed into its tiny view. * * Returns: None. * Side effects: None. */ void ppg_visualizer_set_is_important (PpgVisualizer *visualizer, gboolean important) { g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); visualizer->priv->important = important; g_object_notify(G_OBJECT(visualizer), "is-important"); }
/** * ppg_visualizer_set_end_time: * @visualizer: (in): A #PpgVisualizer. * @end_time: (in): A #gdouble containing the new end time. * * Sets the end_time for the visualizer. A new draw request is queued. * * Returns: None. * Side effects: None. */ void ppg_visualizer_set_end_time (PpgVisualizer *visualizer, gdouble end_time) { g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); g_return_if_fail(end_time >= 0.0); visualizer->priv->end_time = end_time; ppg_visualizer_queue_draw(visualizer); }
/** * ppg_visualizer_set_title: * @visualizer: (in): A #PpgVisualizer. * @title: (in): A string containing the visualizer title. * * Sets the title for the visualizer as viewd by the user. * * Returns: None. * Side effects: None. */ static void ppg_visualizer_set_title (PpgVisualizer *visualizer, const gchar *title) { g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); g_return_if_fail(visualizer->priv->title == NULL); visualizer->priv->title = g_strdup(title); g_object_notify(G_OBJECT(visualizer), "title"); }
static void ppg_instrument_visualizer_added (PpgInstrument *instrument, PpgVisualizer *visualizer) { PpgInstrumentPrivate *priv; g_return_if_fail(PPG_IS_INSTRUMENT(instrument)); g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); priv = instrument->priv; priv->visualizers = g_list_prepend(priv->visualizers, visualizer); }
/** * ppg_visualizer_thaw: * @visualizer: (in): A #PpgVisualizer. * * Thaws a call to ppg_visualizer_freeze() and queues a new draw request of * the visualizer. * * Returns: None. * Side effects: None. */ void ppg_visualizer_thaw (PpgVisualizer *visualizer) { PpgVisualizerPrivate *priv; g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); priv = visualizer->priv; priv->frozen = FALSE; ppg_visualizer_queue_draw(visualizer); }
/** * ppg_visualizer_set_natural_height: * @visualizer: (in): A #PpgVisualizer. * @natural_height: (in): The natural height of the visualizer. * * Sets the natural height for the visualizer. This is what most * implementations will want to modify if they need to adjust their * height based on the content. The zoom level in the instrument * will be multiplied against this value to get the effective * height for the visualizer. * * Returns: None. * Side effects: None. */ static void ppg_visualizer_set_natural_height (PpgVisualizer *visualizer, gdouble natural_height) { PpgVisualizerPrivate *priv; g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); priv = visualizer->priv; priv->natural_height = natural_height; g_object_notify(G_OBJECT(visualizer), "natural-height"); }
/** * ppg_visualizer_queue_resize: * @visualizer: (in): A #PpgVisualizer. * * Queues a request to resize the pattern the visualizer uses to render. * * Returns: None. * Side effects: None. */ static void ppg_visualizer_queue_resize (PpgVisualizer *visualizer) { PpgVisualizerPrivate *priv; g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); priv = visualizer->priv; if (!priv->resize_handler) { priv->resize_handler = g_timeout_add(0, ppg_visualizer_resize_timeout, visualizer); } }
/** * ppg_visualizer_freeze: * @visualizer: (in): A #PpgVisualizer. * * Freezes the visualizer preventing it from drawing updates. Drawing will * continue when ppg_visualizer_thaw() is called. * * Returns: None. * Side effects: None. */ void ppg_visualizer_freeze (PpgVisualizer *visualizer) { PpgVisualizerPrivate *priv; g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); priv = visualizer->priv; priv->frozen = TRUE; if (priv->draw_handler) { g_source_remove(priv->draw_handler); priv->draw_handler = 0; priv->draw_begin_time = 0.0; priv->draw_end_time = 0.0; } }
/** * ppg_visualizer_resize_timeout: * @visualizer: (in): A #PpgVisualizer. * * A GSourceFunc to handle a resize request. The surface of the visualizer * is resized and a draw request is queued. * * Returns: %FALSE always * Side effects: None. */ static gboolean ppg_visualizer_resize_timeout (gpointer data) { PpgVisualizer *visualizer = (PpgVisualizer *)data; PpgVisualizerPrivate *priv; cairo_pattern_t *pattern; GdkWindow *window; GooCanvas *canvas; gdouble width; gdouble height; g_return_val_if_fail(PPG_IS_VISUALIZER(visualizer), FALSE); priv = visualizer->priv; /* * Remove existing surface. */ if (priv->surface) { g_object_set(visualizer, "pattern", NULL, NULL); cairo_surface_destroy(priv->surface); priv->surface = NULL; } /* * Create new surface matching new size allocation. */ g_object_get(visualizer, "height", &height, "width", &width, NULL); canvas = goo_canvas_item_get_canvas(GOO_CANVAS_ITEM(visualizer)); window = gtk_widget_get_window(GTK_WIDGET(canvas)); priv->surface = gdk_window_create_similar_surface(window, CAIRO_CONTENT_COLOR_ALPHA, width, height); /* * Create a new pattern for drawing the item. */ pattern = cairo_pattern_create_for_surface(priv->surface); g_object_set(visualizer, "pattern", pattern, NULL); cairo_pattern_destroy(pattern); ppg_visualizer_queue_draw(visualizer); priv->resize_handler = 0; return FALSE; }
/** * ppg_visualizer_set_time: * @visualizer: (in): A #PpgVisualizer. * @begin_time: (in): A #gdouble containing the new begin time. * @end_time: (in): A #gdouble containing the new end time. * * Sets both the begin_time and end_time for the visualizer. A new draw * request is queued. * * Returns: None. * Side effects: None. */ void ppg_visualizer_set_time (PpgVisualizer *visualizer, gdouble begin_time, gdouble end_time) { PpgVisualizerPrivate *priv; g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); g_return_if_fail(begin_time >= 0.0); g_return_if_fail(end_time >= begin_time); priv = visualizer->priv; if ((priv->begin_time != begin_time) || (priv->end_time != end_time)) { priv->begin_time = begin_time; priv->end_time = end_time; ppg_visualizer_queue_draw(visualizer); } }
void ppg_instrument_remove_visualizer (PpgInstrument *instrument, PpgVisualizer *visualizer) { PpgInstrumentPrivate *priv; g_return_if_fail(PPG_IS_INSTRUMENT(instrument)); g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); priv = instrument->priv; if (!g_list_find(priv->visualizers, visualizer)) { g_critical("Instrument does not contain visualizer instance!"); return; } priv->visualizers = g_list_remove(priv->visualizers, visualizer); g_signal_emit(instrument, signals[VISUALIZER_REMOVED], 0, visualizer); }
/** * ppg_visualizer_queue_draw_time_span: * @visualizer: (in): A #PpgVisualizer. * @begin_time: (in): A #gdouble contianing the beggining time. * @end_time: (in): A #gdouble contianing the ending time. * * Queues a draw for a particular time span. If @begin_time and #end_time * are 0.0, then the entire visibile area will be drawn. * * Returns: None. * Side effects: None. */ void ppg_visualizer_queue_draw_time_span (PpgVisualizer *visualizer, gdouble begin_time, gdouble end_time, gboolean now) { PpgVisualizerPrivate *priv; guint msec; g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); g_return_if_fail(begin_time >= 0.0); g_return_if_fail(end_time >= 0.0); priv = visualizer->priv; if (begin_time == 0.0) { begin_time = priv->begin_time; } if (end_time == 0.0) { end_time = priv->end_time; } if (!priv->frozen) { if (now) { priv->draw_begin_time = begin_time; priv->draw_end_time = end_time; ppg_visualizer_draw_timeout(visualizer); } else if (!priv->draw_handler) { msec = 1000 / priv->frame_limit; priv->draw_begin_time = begin_time; priv->draw_end_time = end_time; priv->draw_handler = g_timeout_add(msec, ppg_visualizer_draw_timeout, visualizer); } else { priv->draw_begin_time = MIN(priv->draw_begin_time, begin_time); priv->draw_end_time = MAX(priv->draw_end_time, end_time); } } }
/** * ppg_instrument_view_visualizer_added: * @view: (in): A #PpgInstrumentView. * * Handle the "visualizer-added" event for the #PpgInstrument. Add the * visualizer to our table of visualizers. * * Returns: None. * Side effects: None. */ static void ppg_instrument_view_visualizer_added (PpgInstrumentView *view, PpgVisualizer *visualizer, PpgInstrument *instrument) { PpgInstrumentViewPrivate *priv; GooCanvasItem *item = (GooCanvasItem *)visualizer; gdouble width; g_return_if_fail(PPG_IS_INSTRUMENT_VIEW(view)); g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); g_return_if_fail(PPG_IS_INSTRUMENT(instrument)); priv = view->priv; g_object_get(view, "width", &width, NULL); width = MAX(200.0, width - HEADER_WIDTH); g_object_set(item, "parent", priv->table, "width", width, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); g_object_bind_property_full(view, "width", item, "width", 0, ppg_instrument_view_transform_width, NULL, NULL, NULL); goo_canvas_item_set_child_properties(priv->table, item, "bottom-padding", ROW_SPACING, "column", 0, "row", priv->visualizers->len, NULL); g_ptr_array_add(priv->visualizers, visualizer); g_signal_connect_swapped(visualizer, "notify::natural-height", G_CALLBACK(ppg_instrument_view_notify_natural_height), view); ppg_instrument_view_relayout(view); }
/** * ppg_visualizer_task_notify_state: * @visualizer: (in): A #PpgVisualizer. * @pspec: (in): A #GParamSpec. * @task: (in): A #PpgTask. * * Handle the "notify::state" signal from @task. Update the visualizer * pattern if necessary. * * Returns: None. * Side effects: None. */ static void ppg_visualizer_task_notify_state (PpgVisualizer *visualizer, GParamSpec *pspec, PpgTask *task) { PpgVisualizerPrivate *priv; cairo_surface_t *surface; PpgTaskState state; cairo_t *cr; gdouble begin_time; gdouble height; gdouble total_width; gdouble span; gdouble width; gdouble x; gdouble y; g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); g_return_if_fail(PPG_IS_TASK(task)); g_return_if_fail(visualizer->priv->surface); priv = visualizer->priv; /* * We don't own the reference, so safe to just drop our pointer. Using * GObjects weak pointers here would be a lot of maintenance pain. */ if (priv->task == task) { priv->task = NULL; } g_object_get(task, "state", &state, "surface", &surface, NULL); if (state == PPG_TASK_SUCCESS) { g_object_get(task, "begin-time", &begin_time, "height", &height, "width", &width, "x", &x, "y", &y, NULL); span = priv->end_time - priv->begin_time; g_object_get(visualizer, "width", &total_width, NULL); x = (begin_time - priv->begin_time) / span * total_width; /* * Only draw what we can do on integer aligned offsets. * * TODO: We need to make sure we render extra area to prevent * a striping effect. */ width -= ceil(x) - x; x = ceil(x); cr = cairo_create(priv->surface); cairo_set_source_surface(cr, surface, x, y); if (cairo_status(cr) != 0) { cairo_destroy(cr); GOTO(failure); } /* * Clip the range of the draw. */ cairo_rectangle(cr, x, y, width, height); cairo_clip_preserve(cr); /* * Clear the draw area. */ cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_fill_preserve(cr); cairo_restore(cr); /* * Fill in the rendered image. */ cairo_fill(cr); cairo_destroy(cr); goo_canvas_item_request_update(GOO_CANVAS_ITEM(visualizer)); } failure: /* * Release our surface that was allocated for the draw request. */ if ((state & PPG_TASK_FINISHED_MASK) != 0) { cairo_surface_destroy(surface); } }
/** * ppg_visualizer_get_name: * @visualizer: (in): A #PpgVisualizer. * * Retrieves the name of a visualizer. * * Returns: None. * Side effects: None. */ const gchar* ppg_visualizer_get_name (PpgVisualizer *visualizer) { g_return_val_if_fail(PPG_IS_VISUALIZER(visualizer), NULL); return visualizer->priv->name; }
/** * ppg_visualizer_draw_timeout: * @visualizer: (in): A #PpgVisualizer. * * A GSourceFunc to start a new drawing task. * * Returns: %FALSE always. * Side effects: None. */ static gboolean ppg_visualizer_draw_timeout (gpointer data) { PpgVisualizer *visualizer = (PpgVisualizer *)data; PpgVisualizerPrivate *priv; cairo_surface_t *surface = NULL; gpointer instance; gdouble begin_time; gdouble end_time; gdouble height; gdouble span; gdouble width; gdouble x; g_return_val_if_fail(PPG_IS_VISUALIZER(visualizer), FALSE); priv = visualizer->priv; /* * Cancel any active tasks. The task will lose its reference after * the task scheduler completes; so we can just NULL it out. */ if ((instance = priv->task)) { priv->task = NULL; ppg_task_cancel(instance); } /* * Make sure we have a time range to even render. */ if (priv->begin_time == 0.0 && priv->end_time == 0.0) { goto cleanup; } /* * Get the time range for the render. */ begin_time = CLAMP(priv->draw_begin_time, 0.0, priv->end_time); end_time = CLAMP(priv->draw_end_time, 0.0, priv->end_time); if (begin_time == 0.0 && end_time == 0.0) { begin_time = priv->begin_time; end_time = priv->end_time; } /* * Get the area for the render. This could probably be optimized to * remove the division by keeping a ratio of (time range / width). */ g_object_get(visualizer, "height", &height, "width", &width, NULL); span = priv->end_time - priv->begin_time; x = (begin_time - priv->begin_time) / span * width; width = ((end_time - priv->begin_time) / span * width) - x; /* * Create a surface for the rendering. */ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); /* * Create the task to do the rendering. */ priv->task = PPG_VISUALIZER_GET_CLASS(visualizer)-> draw(visualizer, cairo_surface_reference(surface), begin_time, end_time, 0, 0, width, height); g_signal_connect_swapped(priv->task, "notify::state", G_CALLBACK(ppg_visualizer_task_notify_state), visualizer); ppg_task_schedule(priv->task); cleanup: if (surface) { cairo_surface_destroy(surface); } priv->draw_handler = 0; priv->draw_begin_time = 0.0; priv->draw_end_time = 0.0; return FALSE; }
/** * ppg_visualizer_queue_draw: * @visualizer: (in): A #PpgVisualizer. * * Queues a draw request for the entire visible area of the visualizer. * * Returns: None. * Side effects: None. */ void ppg_visualizer_queue_draw (PpgVisualizer *visualizer) { g_return_if_fail(PPG_IS_VISUALIZER(visualizer)); ppg_visualizer_queue_draw_time_span(visualizer, 0.0, 0.0, TRUE); }
/** * ppg_visualizer_get_is_important: * @visualizer: (in): A #PpgVisualizer. * * Retrieves if this visualizer is important. See * ppg_visualizer_set_is_important() for more information. * * Returns: %TRUE if the visualizer is important. * Side effects: None. */ gboolean ppg_visualizer_get_is_important (PpgVisualizer *visualizer) { g_return_val_if_fail(PPG_IS_VISUALIZER(visualizer), FALSE); return visualizer->priv->important; }
/** * ppg_visualizer_get_natural_height: * @visualizer: (in): A #PpgVisualizer. * * Gets the natural height of the visualizer. This is used to calculate * the height needed within the instrument view. * * Returns: A #gdouble. * Side effects: None. */ gdouble ppg_visualizer_get_natural_height (PpgVisualizer *visualizer) { g_return_val_if_fail(PPG_IS_VISUALIZER(visualizer), 0.0); return visualizer->priv->natural_height; }