GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 40.1% 73 / 0 / 182
Functions: 56.5% 13 / 0 / 23
Branches: 23.0% 17 / 0 / 74

shumate/shumate-marker.c
Line Branch Exec Source
1 /*
2 * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
3 * Copyright (C) 2011-2013 Jiri Techet <techet@gmail.com>
4 * Copyright (C) 2019 Marcus Lundblad <ml@update.uu.se>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * ShumateMarker:
23 *
24 * Markers represent points of interest on a map. Markers need to be
25 * placed on a layer (a [class@MarkerLayer]). Layers have to be added to a
26 * [class@Map] for the markers to show on the map.
27 *
28 * A marker is nothing more than a regular [class@Gtk.Widget]. You can draw on
29 * it what ever you want. Set the marker's position on the map using
30 * [method@Location.set_location].
31 *
32 * This is a base class of all markers. A typical usage of a marker is for
33 * instance to add a [class@Gtk.Image] with a pin image and add the
34 * [class@Gtk.GestureClick] controller to listen to click events and show
35 * a [class@Gtk.Popover] with the description of the marker.
36 *
37 * ## CSS nodes
38 *
39 * `ShumateMarker` has a single CSS node with the name “map-marker”.
40 */
41
42 #include "shumate-marker.h"
43 #include "shumate-marker-private.h"
44
45 #include "shumate.h"
46 #include "shumate-marshal.h"
47 #include "shumate-tile.h"
48
49 #include <glib.h>
50 #include <glib-object.h>
51 #include <gtk/gtk.h>
52
53 enum
54 {
55 PROP_SELECTABLE = 1,
56 PROP_CHILD,
57 PROP_X_HOTSPOT,
58 PROP_Y_HOTSPOT,
59 N_PROPERTIES,
60
61 PROP_LONGITUDE,
62 PROP_LATITUDE,
63 };
64
65 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
66
67 typedef struct
68 {
69 double lon;
70 double lat;
71 double x_hotspot;
72 double y_hotspot;
73
74 gboolean selected;
75
76 gboolean selectable;
77
78 float click_x;
79 float click_y;
80 gboolean moved;
81
82 GtkWidget *child;
83 } ShumateMarkerPrivate;
84
85 static void location_interface_init (ShumateLocationInterface *iface);
86 static void buildable_interface_init (GtkBuildableIface *iface);
87
88
4/6
shumate_marker_class_intern_init:
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 5 not taken.
shumate_marker_get_type:
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 7 taken 360 times.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 7 not taken.
826 G_DEFINE_TYPE_WITH_CODE (ShumateMarker, shumate_marker, GTK_TYPE_WIDGET,
89 G_ADD_PRIVATE (ShumateMarker)
90 G_IMPLEMENT_INTERFACE (SHUMATE_TYPE_LOCATION, location_interface_init)
91 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_interface_init));
92
93 static GtkBuildableIface *parent_buildable_iface;
94
95 static void
96 ✗ shumate_marker_set_location (ShumateLocation *location,
97 double latitude,
98 double longitude)
99 {
100 ✗ ShumateMarker *marker = (ShumateMarker *) location;
101 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
102
103 ✗ g_assert (SHUMATE_IS_MARKER (location));
104
105 ✗ priv->lon = CLAMP (longitude, SHUMATE_MIN_LONGITUDE, SHUMATE_MAX_LONGITUDE);
106 ✗ priv->lat = CLAMP (latitude, SHUMATE_MIN_LATITUDE, SHUMATE_MAX_LATITUDE);
107
108 ✗ g_object_notify (G_OBJECT (location), "latitude");
109 ✗ g_object_notify (G_OBJECT (location), "longitude");
110 ✗ }
111
112
113 static double
114 ✗ shumate_marker_get_latitude (ShumateLocation *location)
115 {
116 ✗ ShumateMarker *marker = (ShumateMarker *) location;
117 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
118
119 ✗ g_assert (SHUMATE_IS_MARKER (location));
120
121 ✗ return priv->lat;
122 }
123
124
125 static double
126 ✗ shumate_marker_get_longitude (ShumateLocation *location)
127 {
128 ✗ ShumateMarker *marker = (ShumateMarker *) location;
129 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
130
131 ✗ g_assert (SHUMATE_IS_MARKER (location));
132
133 ✗ return priv->lon;
134 }
135
136 static void
137 ✗ shumate_marker_add_child (GtkBuildable *buildable,
138 GtkBuilder *builder,
139 GObject *child,
140 const char *type)
141 {
142 ✗ if (GTK_IS_WIDGET (child))
143 ✗ shumate_marker_set_child (SHUMATE_MARKER (buildable), GTK_WIDGET (child));
144 else
145 ✗ parent_buildable_iface->add_child (buildable, builder, child, type);
146 ✗ }
147
148 static void
149 ✗ shumate_marker_get_property (GObject *object,
150 guint prop_id,
151 GValue *value,
152 GParamSpec *pspec)
153 {
154 ✗ ShumateMarker *marker = SHUMATE_MARKER (object);
155 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
156
157 ✗ switch (prop_id)
158 {
159 ✗ case PROP_LONGITUDE:
160 ✗ g_value_set_double (value, priv->lon);
161 ✗ break;
162
163 ✗ case PROP_LATITUDE:
164 ✗ g_value_set_double (value, priv->lat);
165 ✗ break;
166
167 ✗ case PROP_X_HOTSPOT:
168 ✗ g_value_set_double (value, priv->x_hotspot);
169 ✗ break;
170
171 ✗ case PROP_Y_HOTSPOT:
172 ✗ g_value_set_double (value, priv->y_hotspot);
173 ✗ break;
174
175 ✗ case PROP_SELECTABLE:
176 ✗ g_value_set_boolean (value, priv->selectable);
177 ✗ break;
178
179 ✗ case PROP_CHILD:
180 ✗ g_value_set_object (value, priv->child);
181 ✗ break;
182
183 ✗ default:
184 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
185 }
186 ✗ }
187
188
189 static void
190 ✗ shumate_marker_set_property (GObject *object,
191 guint prop_id,
192 const GValue *value,
193 GParamSpec *pspec)
194 {
195 ✗ ShumateMarker *marker = SHUMATE_MARKER (object);
196 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
197
198 ✗ switch (prop_id)
199 {
200 ✗ case PROP_LONGITUDE:
201 {
202 ✗ double lon = g_value_get_double (value);
203 ✗ shumate_marker_set_location (SHUMATE_LOCATION (marker), priv->lat, lon);
204 ✗ break;
205 }
206
207 ✗ case PROP_LATITUDE:
208 {
209 ✗ double lat = g_value_get_double (value);
210 ✗ shumate_marker_set_location (SHUMATE_LOCATION (marker), lat, priv->lon);
211 ✗ break;
212 }
213
214 ✗ case PROP_X_HOTSPOT:
215 ✗ shumate_marker_set_hotspot (marker, g_value_get_double (value), priv->y_hotspot);
216 ✗ break;
217
218 ✗ case PROP_Y_HOTSPOT:
219 ✗ shumate_marker_set_hotspot (marker, priv->x_hotspot, g_value_get_double (value));
220 ✗ break;
221
222 ✗ case PROP_SELECTABLE:
223 {
224 ✗ gboolean bvalue = g_value_get_boolean (value);
225 ✗ shumate_marker_set_selectable (marker, bvalue);
226 ✗ break;
227 }
228
229 ✗ case PROP_CHILD:
230 {
231 ✗ GtkWidget *child = g_value_get_object (value);
232 ✗ shumate_marker_set_child (marker, child);
233 ✗ break;
234 }
235
236 ✗ default:
237 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
238 }
239 ✗ }
240
241 static void
242 103 shumate_marker_dispose (GObject *object)
243 {
244 103 ShumateMarker *marker = SHUMATE_MARKER (object);
245
246 103 shumate_marker_set_child (marker, NULL);
247
248 103 G_OBJECT_CLASS (shumate_marker_parent_class)->dispose (object);
249 103 }
250
251 static void
252 3 shumate_marker_class_init (ShumateMarkerClass *klass)
253 {
254 3 GObjectClass *object_class = G_OBJECT_CLASS (klass);
255 3 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
256
257 3 object_class->get_property = shumate_marker_get_property;
258 3 object_class->set_property = shumate_marker_set_property;
259 3 object_class->dispose = shumate_marker_dispose;
260
261 /**
262 * ShumateMarker:child:
263 *
264 * The child widget of the marker
265 */
266 6 obj_properties[PROP_CHILD] =
267 3 g_param_spec_object ("child", NULL, NULL,
268 GTK_TYPE_WIDGET,
269 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
270
271 /**
272 * ShumateMarker:selectable:
273 *
274 * The selectable state of the marker
275 */
276 6 obj_properties[PROP_SELECTABLE] =
277 3 g_param_spec_boolean ("selectable", NULL, NULL,
278 FALSE,
279 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
280
281 /**
282 * ShumateMarker:x-hotspot:
283 *
284 * The x hotspot of the marker, a negative value means that the actual
285 * x hotspot is calculated with the [property@Gtk.Widget:halign] property.
286 * The x hotspot should not be more than the width of the widget.
287 *
288 * Since: 1.5
289 */
290 6 obj_properties[PROP_X_HOTSPOT] =
291 3 g_param_spec_double ("x-hotspot", NULL, NULL,
292 -G_MAXDOUBLE, G_MAXDOUBLE, -1,
293 G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
294
295 /**
296 * ShumateMarker:y-hotspot:
297 *
298 * The y hotspot of the marker, a negative value means that the actual
299 * y hotspot is calculated with the [property@Gtk.Widget:valign] property.
300 * The y hotspot should not be more than the height of the widget.
301 *
302 * Since: 1.5
303 */
304 6 obj_properties[PROP_Y_HOTSPOT] =
305 3 g_param_spec_double ("y-hotspot", NULL, NULL,
306 -G_MAXDOUBLE, G_MAXDOUBLE, -1,
307 G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
308
309 3 g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties);
310
311 3 g_object_class_override_property (object_class,
312 PROP_LONGITUDE,
313 "longitude");
314
315 3 g_object_class_override_property (object_class,
316 PROP_LATITUDE,
317 "latitude");
318
319 3 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
320 3 gtk_widget_class_set_css_name (widget_class, "map-marker");
321 3 }
322
323 static void
324 105 shumate_marker_init (ShumateMarker *self)
325 {
326 105 ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (self);
327
328 105 priv->lat = 0;
329 105 priv->lon = 0;
330 105 priv->selected = FALSE;
331 105 priv->selectable = TRUE;
332 105 priv->x_hotspot = -1;
333 105 priv->y_hotspot = -1;
334 105 }
335
336 static void
337 3 location_interface_init (ShumateLocationInterface *iface)
338 {
339 3 iface->get_latitude = shumate_marker_get_latitude;
340 3 iface->get_longitude = shumate_marker_get_longitude;
341 3 iface->set_location = shumate_marker_set_location;
342 3 }
343
344 static void
345 3 buildable_interface_init (GtkBuildableIface *iface)
346 {
347 3 parent_buildable_iface = g_type_interface_peek_parent (iface);
348 3 iface->add_child = shumate_marker_add_child;
349 3 }
350
351 /**
352 * shumate_marker_new:
353 *
354 * Creates an instance of #ShumateMarker.
355 *
356 * Returns: a new #ShumateMarker.
357 */
358 ShumateMarker *
359 1 shumate_marker_new (void)
360 {
361 1 return SHUMATE_MARKER (g_object_new (SHUMATE_TYPE_MARKER, NULL));
362 }
363
364 /**
365 * shumate_marker_is_selected:
366 * @marker: a #ShumateMarker
367 *
368 * Checks whether the marker is selected.
369 *
370 * Returns: %TRUE if the marker is selected, otherwise %FALSE
371 */
372 gboolean
373 125 shumate_marker_is_selected (ShumateMarker *marker)
374 {
375 125 ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
376
377
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 125 times.
125 g_return_val_if_fail (SHUMATE_IS_MARKER (marker), FALSE);
378
379 125 return priv->selected;
380 }
381
382
383 /**
384 * shumate_marker_set_selectable:
385 * @marker: a #ShumateMarker
386 * @value: the selectable state
387 *
388 * Sets the marker as selectable or not.
389 */
390 void
391 ✗ shumate_marker_set_selectable (ShumateMarker *marker,
392 gboolean value)
393 {
394 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
395
396 ✗ g_return_if_fail (SHUMATE_IS_MARKER (marker));
397
398 ✗ priv->selectable = value;
399
400 ✗ g_object_notify_by_pspec (G_OBJECT (marker), obj_properties[PROP_SELECTABLE]);
401 }
402
403
404 /**
405 * shumate_marker_get_selectable:
406 * @marker: a #ShumateMarker
407 *
408 * Checks whether the marker is selectable.
409 *
410 * Returns: the selectable or not state of the marker.
411 */
412 gboolean
413 8 shumate_marker_get_selectable (ShumateMarker *marker)
414 {
415 8 ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
416
417
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 8 times.
8 g_return_val_if_fail (SHUMATE_IS_MARKER (marker), FALSE);
418
419 8 return priv->selectable;
420 }
421
422 /**
423 * shumate_marker_get_child:
424 * @marker: a #ShumateMarker
425 *
426 * Retrieves the current child of @marker.
427 *
428 * Returns: (transfer none) (nullable): a #GtkWidget.
429 */
430 GtkWidget *
431 ✗ shumate_marker_get_child (ShumateMarker *marker)
432 {
433 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
434
435 ✗ g_return_val_if_fail (SHUMATE_IS_MARKER (marker), NULL);
436
437 ✗ return priv->child;
438 }
439
440 /**
441 * shumate_marker_set_child:
442 * @marker: a #ShumateMarker
443 * @child: (nullable): a #GtkWidget
444 *
445 * Sets the child widget of @marker.
446 */
447 void
448 105 shumate_marker_set_child (ShumateMarker *marker,
449 GtkWidget *child)
450 {
451 105 ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
452
453
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 105 times.
105 g_return_if_fail (SHUMATE_IS_MARKER (marker));
454
455
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 11 taken 103 times.
105 if (priv->child == child)
456 return;
457
458
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 1 time.
2 g_clear_pointer (&priv->child, gtk_widget_unparent);
459
460 2 priv->child = child;
461
462
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 10 taken 1 time.
2 if (priv->child)
463 1 gtk_widget_set_parent (priv->child, GTK_WIDGET (marker));
464
465 2 g_object_notify_by_pspec (G_OBJECT (marker), obj_properties[PROP_CHILD]);
466 }
467
468 /**
469 * PRIVATE:shumate_marker_set_selected:
470 * @marker: a #ShumateMarker
471 * @value: %TRUE to select the marker, %FALSE to unselect it
472 *
473 * Sets the selected state flag of the marker widget.
474 */
475 void
476 114 shumate_marker_set_selected (ShumateMarker *marker, gboolean value)
477 {
478 114 ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
479
480
2/2
✓ Branch 2 → 3 taken 10 times.
✓ Branch 2 → 6 taken 104 times.
114 if (priv->selected == value)
481 return;
482
483 10 priv->selected = value;
484
485
2/2
✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 5 taken 5 times.
10 if (value) {
486 5 gtk_widget_set_state_flags (GTK_WIDGET (marker),
487 GTK_STATE_FLAG_SELECTED, FALSE);
488 } else {
489 5 gtk_widget_unset_state_flags (GTK_WIDGET (marker),
490 GTK_STATE_FLAG_SELECTED);
491 }
492 }
493
494 /**
495 * shumate_marker_set_hotspot:
496 * @marker: a #ShumateMarker
497 * @x_hotspot: the x hotspot
498 * @y_hotspot: the y hotspot
499 *
500 * Set the hotspot point for the given marker. The value is in pixel relative
501 * to the top-left corner. Use any negative value to fallback to the
502 * [property@Gtk.Widget:halign] or [property@Gtk.Widget:valign] properties.
503 *
504 * Since: 1.5
505 */
506 void
507 ✗ shumate_marker_set_hotspot (ShumateMarker *marker,
508 gdouble x_hotspot,
509 gdouble y_hotspot)
510 {
511 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
512
513 ✗ g_return_if_fail (SHUMATE_IS_MARKER (marker));
514
515 ✗ if (x_hotspot < 0)
516 ✗ x_hotspot = -1;
517
518 ✗ if (y_hotspot < 0)
519 ✗ y_hotspot = -1;
520
521 ✗ g_object_freeze_notify (G_OBJECT (marker));
522 ✗ if (x_hotspot != priv->x_hotspot) {
523 ✗ priv->x_hotspot = x_hotspot;
524 ✗ g_object_notify_by_pspec (G_OBJECT (marker), obj_properties[PROP_X_HOTSPOT]);
525 }
526
527 ✗ if (y_hotspot != priv->y_hotspot) {
528 ✗ priv->y_hotspot = y_hotspot;
529 ✗ g_object_notify_by_pspec (G_OBJECT (marker), obj_properties[PROP_Y_HOTSPOT]);
530 }
531
532 ✗ g_object_thaw_notify (G_OBJECT (marker));
533 }
534
535 /**
536 * shumate_marker_get_hotspot:
537 * @marker: a #ShumateMarker
538 * @x_hotspot: (out) (optional): return value for the x hotspot
539 * @y_hotspot: (out) (optional): return value for the y hotspot
540 *
541 * Get the hotspot point for the given marker. The value is in pixel relative
542 * to the top-left corner. Any negative value means that the hotspot get
543 * computed with the [property@Gtk.Widget:halign] or [property@Gtk.Widget:valign]
544 * properties.
545 *
546 * Since: 1.5
547 */
548 ✗ void shumate_marker_get_hotspot (ShumateMarker *marker,
549 gdouble *x_hotspot,
550 gdouble *y_hotspot)
551 {
552 ✗ ShumateMarkerPrivate *priv = shumate_marker_get_instance_private (marker);
553
554 ✗ g_return_if_fail (SHUMATE_IS_MARKER (marker));
555
556 ✗ if (x_hotspot)
557 ✗ *x_hotspot = priv->x_hotspot;
558
559 ✗ if (y_hotspot)
560 ✗ *y_hotspot = priv->y_hotspot;
561 }
562