/* * draw-layer.c * Copyright (C) 2008-2009 Pierre-Luc Beaudoin * Copyright (C) 2011-2013 Jiri Techet * Copyright (C) 2019 Marcus Lundblad * Copyright (C) 2021 Zwarf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Code mainly taken from: * https://gitlab.gnome.org/GNOME/libshumate/-/blob/main/shumate/shumate-path-layer.c * https://gitlab.gnome.org/GNOME/libshumate/-/blob/main/shumate/shumate-marker-layer.c * This is a combination of the 'ShumatePathLayer' and 'ShumateMarkerLayer' classes adjusted for the needs of PicPlanner. * * TODO: * Comment better what is happening inside the functions. */ #include "draw-layer.h" static GdkRGBA DEFAULT_STROKE_COLOR = { 0.64, 0.0, 0.0, 1.0 }; typedef struct { GdkRGBA *stroke_color; gboolean stroke; double stroke_width; double *nodes_coordinates; uint nodes_len; } PicplannerDrawLayerPrivate; G_DEFINE_TYPE_WITH_PRIVATE (PicplannerDrawLayer, picplanner_draw_layer, SHUMATE_TYPE_LAYER); static void update_marker_visibility (PicplannerDrawLayer *layer, PicplannerMarker *marker) { ShumateViewport *viewport; ShumateMapSource *map_source; gboolean within_viewport; double x, y, min_size, x_offset, y_offset, x_marker_offset, y_marker_offset; int marker_width, marker_height; int width, height; g_assert (PICPLANNER_IS_DRAW_LAYER (layer)); viewport = shumate_layer_get_viewport (SHUMATE_LAYER (layer)); map_source = shumate_viewport_get_reference_map_source (viewport); if (!map_source) return; x = picplanner_marker_get_x (marker); y = picplanner_marker_get_y (marker); x_marker_offset = picplanner_marker_get_x_offset (marker); y_marker_offset = picplanner_marker_get_y_offset (marker); width = gtk_widget_get_width (GTK_WIDGET (layer)); height = gtk_widget_get_height (GTK_WIDGET (layer)); if (width -marker_width && x <= width && y > -marker_height && y <= height && marker_width < width && marker_height < height; gtk_widget_set_child_visible (GTK_WIDGET (marker), within_viewport); if (within_viewport) { GtkAllocation marker_allocation; gtk_widget_get_allocation (GTK_WIDGET (marker), &marker_allocation); if (marker_allocation.x != (int)x || marker_allocation.y != (int)y) { gtk_widget_queue_allocate (GTK_WIDGET (layer)); } } } static void picplanner_draw_layer_size_allocate (GtkWidget *widget, int width, int height, int baseline) { PicplannerDrawLayer *self = PICPLANNER_DRAW_LAYER (widget); GtkAllocation allocation; GtkWidget *child; (void) baseline; for (child = gtk_widget_get_first_child (GTK_WIDGET (self)); child != NULL; child = gtk_widget_get_next_sibling (child)) { gboolean within_viewport; double x, y, min_size, x_offset, y_offset, x_marker_offset, y_marker_offset; int marker_width, marker_height; if (!gtk_widget_should_layout (child)) continue; x = picplanner_marker_get_x (PICPLANNER_MARKER (child)); y = picplanner_marker_get_y (PICPLANNER_MARKER (child)); x_marker_offset = picplanner_marker_get_x_offset (PICPLANNER_MARKER (child)); y_marker_offset = picplanner_marker_get_y_offset (PICPLANNER_MARKER (child)); if (width -allocation.width && x <= width && y > -allocation.height && y <= height && allocation.width < width && allocation.height < height; gtk_widget_set_child_visible (child, within_viewport); if (within_viewport) gtk_widget_size_allocate (child, &allocation, -1); } } static void picplanner_draw_layer_dispose (GObject *object) { PicplannerDrawLayer *self = PICPLANNER_DRAW_LAYER (object); PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (self); ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self)); GtkWidget *child; g_signal_handlers_disconnect_by_data (viewport, self); if (priv->nodes_coordinates) picplanner_draw_layer_remove_all (PICPLANNER_DRAW_LAYER (object)); while ((child = gtk_widget_get_first_child (GTK_WIDGET (object)))) gtk_widget_unparent (child); G_OBJECT_CLASS (picplanner_draw_layer_parent_class)->dispose (object); } static void picplanner_draw_layer_finalize (GObject *object) { PicplannerDrawLayer *self = PICPLANNER_DRAW_LAYER (object); PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (self); g_clear_pointer (&priv->stroke_color, gdk_rgba_free); G_OBJECT_CLASS (picplanner_draw_layer_parent_class)->finalize (object); } static void picplanner_draw_layer_constructed (GObject *object) { G_OBJECT_CLASS (picplanner_draw_layer_parent_class)->constructed (object); } static void picplanner_draw_layer_snapshot (GtkWidget *widget, GtkSnapshot *snapshot) { PicplannerDrawLayer *self = (PicplannerDrawLayer *)widget; PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (self); int width, height, min_size, x_offset, y_offset; cairo_t *cr; GtkWidget *child; /* * Draw the lines given by the coordinates. */ width = gtk_widget_get_allocated_width (widget); height = gtk_widget_get_allocated_height (widget); if (widthnodes_len; i++) { double x, y; x = priv->nodes_coordinates[i*2]*min_size+x_offset; y = priv->nodes_coordinates[i*2+1]*min_size+y_offset; cairo_line_to (cr, x, y); } if (priv->stroke) { gdk_cairo_set_source_rgba (cr, priv->stroke_color); cairo_set_line_width (cr, priv->stroke_width); cairo_stroke (cr); } cairo_destroy (cr); /* * Draw the children added to the layer. */ for (child = gtk_widget_get_first_child (widget); child != NULL; child = gtk_widget_get_next_sibling (child)) { gtk_widget_snapshot_child (widget, child, snapshot); } } static void picplanner_draw_layer_class_init (PicplannerDrawLayerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->finalize = picplanner_draw_layer_finalize; object_class->dispose = picplanner_draw_layer_dispose; object_class->constructed = picplanner_draw_layer_constructed; widget_class->snapshot = picplanner_draw_layer_snapshot; widget_class->size_allocate = picplanner_draw_layer_size_allocate; } static void picplanner_draw_layer_init (PicplannerDrawLayer *self) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (self); priv->stroke = TRUE; priv->stroke_width = 2.0; priv->nodes_coordinates = NULL; priv->nodes_len = 0; priv->stroke_color = gdk_rgba_copy (&DEFAULT_STROKE_COLOR); } PicplannerDrawLayer * picplanner_draw_layer_new (ShumateViewport *viewport) { return g_object_new (PICPLANNER_TYPE_DRAW_LAYER, "viewport", viewport, NULL); } void picplanner_draw_layer_add_marker (PicplannerDrawLayer *layer, PicplannerMarker *marker) { g_return_if_fail (PICPLANNER_IS_DRAW_LAYER (layer)); g_return_if_fail (PICPLANNER_IS_MARKER (marker)); gtk_widget_insert_before (GTK_WIDGET(marker), GTK_WIDGET (layer), NULL); update_marker_visibility (layer, marker); } void picplanner_draw_layer_add_node_coordinates (PicplannerDrawLayer *layer, double x, double y) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (layer); g_return_if_fail (PICPLANNER_IS_DRAW_LAYER (layer)); priv->nodes_coordinates = (double *) realloc (priv->nodes_coordinates, sizeof (double) * (priv->nodes_len+1) * 2); priv->nodes_coordinates[priv->nodes_len*2] = x; priv->nodes_coordinates[priv->nodes_len*2+1] = y; priv->nodes_len ++; gtk_widget_queue_draw (GTK_WIDGET (layer)); } void picplanner_draw_layer_remove_all (PicplannerDrawLayer *layer) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (layer); GtkWidget *child; g_return_if_fail (PICPLANNER_IS_DRAW_LAYER (layer)); child = gtk_widget_get_first_child (GTK_WIDGET (layer)); while (child) { GtkWidget *next = gtk_widget_get_next_sibling (child); g_signal_handlers_disconnect_by_data (child, layer); gtk_widget_unparent (child); child = next; } g_clear_pointer (&priv->nodes_coordinates, g_free); priv->nodes_len = 0; gtk_widget_queue_draw (GTK_WIDGET (layer)); } void picplanner_draw_layer_set_stroke_color (PicplannerDrawLayer *layer, const GdkRGBA *color) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (layer); g_return_if_fail (PICPLANNER_IS_DRAW_LAYER (layer)); if (priv->stroke_color != NULL) gdk_rgba_free (priv->stroke_color); if (color == NULL) color = &DEFAULT_STROKE_COLOR; priv->stroke_color = gdk_rgba_copy (color); gtk_widget_queue_draw (GTK_WIDGET (layer)); } GdkRGBA * picplanner_draw_layer_get_stroke_color (PicplannerDrawLayer *layer) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (layer); g_return_val_if_fail (PICPLANNER_IS_DRAW_LAYER (layer), NULL); return priv->stroke_color; } void picplanner_draw_layer_set_stroke (PicplannerDrawLayer *layer, gboolean value) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (layer); g_return_if_fail (PICPLANNER_IS_DRAW_LAYER (layer)); priv->stroke = value; gtk_widget_queue_draw (GTK_WIDGET (layer)); } gboolean picplanner_draw_layer_get_stroke (PicplannerDrawLayer *layer) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (layer); g_return_val_if_fail (PICPLANNER_IS_DRAW_LAYER (layer), FALSE); return priv->stroke; } void picplanner_draw_layer_set_stroke_width (PicplannerDrawLayer *layer, double value) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (layer); g_return_if_fail (PICPLANNER_IS_DRAW_LAYER (layer)); priv->stroke_width = value; gtk_widget_queue_draw (GTK_WIDGET (layer)); } double picplanner_draw_layer_get_stroke_width (PicplannerDrawLayer *layer) { PicplannerDrawLayerPrivate *priv = picplanner_draw_layer_get_instance_private (layer); g_return_val_if_fail (PICPLANNER_IS_DRAW_LAYER (layer), 0); return priv->stroke_width; }