kopia lustrzana https://gitlab.com/Zwarf/picplanner
460 wiersze
13 KiB
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;
|
|
}
|