picplanner/src/map/draw-layer.c

460 wiersze
13 KiB
C

/*
* draw-layer.c
* Copyright (C) 2008-2009 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
* Copyright (C) 2011-2013 Jiri Techet <techet@gmail.com>
* Copyright (C) 2019 Marcus Lundblad <ml@update.uu.se>
* Copyright (C) 2021 Zwarf <zwarf@mail.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* 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<height)
{
min_size = width;
x_offset = 0;
y_offset = (height-width)/2;
}
else
{
min_size = height;
x_offset = (width-height)/2;
y_offset = 0;
}
gtk_widget_measure (GTK_WIDGET (marker), GTK_ORIENTATION_HORIZONTAL, -1, 0, &marker_width, NULL, NULL);
gtk_widget_measure (GTK_WIDGET (marker), GTK_ORIENTATION_VERTICAL, -1, 0, &marker_height, NULL, NULL);
x = floorf (x*min_size + x_offset + x_marker_offset - marker_width/2.f);
y = floorf (y*min_size + y_offset + y_marker_offset- marker_height/2.f);
within_viewport = x > -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<height)
{
min_size = width;
x_offset = 0;
y_offset = (height-width)/2;
}
else
{
min_size = height;
x_offset = (width-height)/2;
y_offset = 0;
}
gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, 0, &marker_width, NULL, NULL);
gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, -1, 0, &marker_height, NULL, NULL);
x = floorf (x*min_size + x_offset + x_marker_offset - marker_width/2.f);
y = floorf (y*min_size + y_offset + y_marker_offset- marker_height/2.f);
allocation.x = x;
allocation.y = y;
allocation.width = marker_width;
allocation.height = marker_height;
within_viewport = x > -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 (width<height)
{
min_size = width;
x_offset = 0;
y_offset = (height-width)/2;
}
else
{
min_size = height;
x_offset = (width-height)/2;
y_offset = 0;
}
if (!gtk_widget_get_visible (widget) || width <= 0 || height <= 0)
return;
cr = gtk_snapshot_append_cairo (snapshot, &GRAPHENE_RECT_INIT(0, 0, width, height));
cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
for (uint i = 0; i < priv->nodes_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;
}