GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 52.3% 157 / 0 / 300
Functions: 66.7% 22 / 0 / 33
Branches: 29.5% 46 / 0 / 156

shumate/shumate-marker-layer.c
Line Branch Exec Source
1 /*
2 * Copyright (C) 2008-2009 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 * ShumateMarkerLayer:
23 *
24 * Displays markers on the map. It is responsible for positioning markers
25 * correctly, marker selections and group marker operations.
26 */
27
28 #include "shumate-marker-layer.h"
29 #include "shumate-marker-private.h"
30 #include "shumate-inspector-settings-private.h"
31
32 #include "shumate-enum-types.h"
33
34 #include <cairo/cairo-gobject.h>
35 #include <glib.h>
36
37 enum
38 {
39 PROP_SELECTION_MODE = 1,
40 N_PROPERTIES
41 };
42
43 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
44
45 enum
46 {
47 MARKER_SELECTED,
48 MARKER_UNSELECTED,
49 LAST_SIGNAL
50 };
51
52 static guint signals[LAST_SIGNAL];
53
54
55 struct _ShumateMarkerLayer
56 {
57 ShumateLayer parent_instance;
58
59 GtkSelectionMode mode;
60 GList *selected;
61
62 int n_children;
63 };
64
65
4/6
shumate_marker_layer_class_intern_init:
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
shumate_marker_layer_get_type:
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 7 taken 242 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 7 not taken.
248 G_DEFINE_TYPE (ShumateMarkerLayer, shumate_marker_layer, SHUMATE_TYPE_LAYER);
66
67 static void
68 ✗ on_click_gesture_released (ShumateMarkerLayer *self,
69 int n_press,
70 double x,
71 double y,
72 GtkGestureClick *gesture)
73 {
74 ✗ GtkWidget *self_widget = GTK_WIDGET (self);
75 ✗ GtkWidget *child;
76 ✗ ShumateMarker *marker;
77
78 ✗ child = gtk_widget_pick (self_widget, x, y, GTK_PICK_DEFAULT);
79 ✗ if (!child)
80 return;
81
82 ✗ while (child != NULL && gtk_widget_get_parent (child) != self_widget)
83 ✗ child = gtk_widget_get_parent (child);
84
85 ✗ marker = SHUMATE_MARKER (child);
86 ✗ if (!marker)
87 return;
88
89 ✗ if (shumate_marker_is_selected (marker)) {
90 ✗ if (self->mode != GTK_SELECTION_BROWSE) {
91 ✗ shumate_marker_layer_unselect_marker (self, marker);
92 }
93 }
94 else {
95 ✗ shumate_marker_layer_select_marker (self, marker);
96 }
97 }
98
99 static void
100 ✗ calculate_local_marker_offset (ShumateMarker *marker,
101 int marker_width, int marker_height,
102 gdouble *x, gdouble *y)
103 {
104 ✗ double hotspot_x, hotspot_y;
105
106 ✗ g_assert (SHUMATE_IS_MARKER (marker));
107
108 ✗ shumate_marker_get_hotspot (marker, &hotspot_x, &hotspot_y);
109 ✗ if (G_UNLIKELY (hotspot_x > marker_width)) {
110 ✗ g_warning ("Marker x hotspot (%lf) is more than the marker width (%d).", hotspot_x, marker_width);
111 ✗ hotspot_x = marker_width;
112 }
113
114 ✗ if (G_UNLIKELY (hotspot_y > marker_height)) {
115 ✗ g_warning ("Marker y hotspot (%lf) is more than the marker height (%d).", hotspot_y, marker_height);
116 ✗ hotspot_y = marker_height;
117 }
118
119 ✗ if (hotspot_x < 0) {
120 ✗ switch (gtk_widget_get_halign (GTK_WIDGET (marker))) {
121 ✗ case GTK_ALIGN_START:
122 ✗ if (gtk_widget_get_direction (GTK_WIDGET (marker)) == GTK_TEXT_DIR_RTL)
123 ✗ *x -= marker_width;
124 break;
125 ✗ case GTK_ALIGN_END:
126 ✗ if (gtk_widget_get_direction (GTK_WIDGET (marker)) != GTK_TEXT_DIR_RTL)
127 ✗ *x -= marker_width;
128 break;
129 ✗ default:
130 ✗ *x = floorf (*x - marker_width/2.f);
131 ✗ break;
132 }
133 } else {
134 ✗ if (gtk_widget_get_direction (GTK_WIDGET (marker)) == GTK_TEXT_DIR_RTL)
135 ✗ *x -= marker_width - hotspot_x;
136 else
137 ✗ *x -= hotspot_x;
138 }
139
140 ✗ if (hotspot_y < 0) {
141 ✗ switch (gtk_widget_get_valign (GTK_WIDGET (marker))) {
142 case GTK_ALIGN_START:
143 break;
144 ✗ case GTK_ALIGN_END:
145 ✗ *y -= marker_height;
146 ✗ break;
147 ✗ default:
148 ✗ *y = floorf (*y - marker_height/2.f);
149 ✗ break;
150 }
151 } else {
152 ✗ *y -= hotspot_y;
153 }
154 ✗ }
155
156 static void
157 104 update_marker_visibility (ShumateMarkerLayer *layer,
158 ShumateMarker *marker)
159 {
160 104 ShumateViewport *viewport;
161 104 ShumateMapSource *map_source;
162 104 gboolean within_viewport;
163 104 double lon, lat;
164 104 double x, y;
165 104 int marker_width, marker_height;
166 104 int width, height;
167
168
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 104 times.
104 g_assert (SHUMATE_IS_MARKER_LAYER (layer));
169
170 104 viewport = shumate_layer_get_viewport (SHUMATE_LAYER (layer));
171 104 map_source = shumate_viewport_get_reference_map_source (viewport);
172
1/2
✓ Branch 7 → 8 taken 104 times.
✗ Branch 7 → 9 not taken.
104 if (!map_source)
173 104 return;
174
175 ✗ lon = shumate_location_get_longitude (SHUMATE_LOCATION (marker));
176 ✗ lat = shumate_location_get_latitude (SHUMATE_LOCATION (marker));
177
178 ✗ width = gtk_widget_get_width (GTK_WIDGET (layer));
179 ✗ height = gtk_widget_get_height (GTK_WIDGET (layer));
180
181 ✗ gtk_widget_measure (GTK_WIDGET (marker), GTK_ORIENTATION_HORIZONTAL, -1, 0, &marker_width, NULL, NULL);
182 ✗ gtk_widget_measure (GTK_WIDGET (marker), GTK_ORIENTATION_VERTICAL, -1, 0, &marker_height, NULL, NULL);
183
184 ✗ shumate_viewport_location_to_widget_coords (viewport, GTK_WIDGET (layer), lat, lon, &x, &y);
185 ✗ calculate_local_marker_offset (marker, marker_width, marker_height, &x, &y);
186
187 ✗ within_viewport = x > -marker_width && x <= width &&
188 ✗ y > -marker_height && y <= height &&
189 ✗ marker_width < width && marker_height < height;
190
191 ✗ gtk_widget_set_child_visible (GTK_WIDGET (marker), within_viewport);
192
193 ✗ if (within_viewport)
194 {
195 ✗ graphene_rect_t bounds;
196
197 ✗ if (gtk_widget_compute_bounds (GTK_WIDGET (marker), GTK_WIDGET (layer), &bounds))
198 ✗ if (bounds.origin.x != (int)x || bounds.origin.y != (int)y)
199 ✗ gtk_widget_queue_allocate (GTK_WIDGET (layer));
200 }
201 }
202
203 static void
204 ✗ shumate_marker_layer_reposition_markers (ShumateMarkerLayer *self)
205 {
206 ✗ GtkWidget *child;
207
208 ✗ for (child = gtk_widget_get_first_child (GTK_WIDGET (self));
209 ✗ child != NULL;
210 ✗ child = gtk_widget_get_next_sibling (child))
211 {
212 ✗ update_marker_visibility (self, SHUMATE_MARKER (child));
213 }
214 ✗ }
215
216 static void
217 ✗ on_viewport_changed (ShumateMarkerLayer *self,
218 GParamSpec *pspec,
219 ShumateViewport *view)
220 {
221 ✗ g_assert (SHUMATE_IS_MARKER_LAYER (self));
222
223 ✗ shumate_marker_layer_reposition_markers (self);
224 ✗ }
225
226 static void
227 ✗ shumate_marker_layer_size_allocate (GtkWidget *widget,
228 int width,
229 int height,
230 int baseline)
231 {
232 ✗ ShumateMarkerLayer *self = SHUMATE_MARKER_LAYER (widget);
233 ✗ ShumateViewport *viewport;
234 ✗ GtkAllocation allocation;
235 ✗ GtkWidget *child;
236
237 ✗ viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
238
239 ✗ for (child = gtk_widget_get_first_child (GTK_WIDGET (self));
240 ✗ child != NULL;
241 ✗ child = gtk_widget_get_next_sibling (child))
242 {
243 ✗ gboolean within_viewport;
244 ✗ double lon, lat;
245 ✗ double x, y;
246 ✗ int marker_width, marker_height;
247
248 ✗ if (!gtk_widget_should_layout (child))
249 ✗ continue;
250
251 ✗ lon = shumate_location_get_longitude (SHUMATE_LOCATION (child));
252 ✗ lat = shumate_location_get_latitude (SHUMATE_LOCATION (child));
253
254 ✗ gtk_widget_measure (child, GTK_ORIENTATION_HORIZONTAL, -1, 0, &marker_width, NULL, NULL);
255 ✗ gtk_widget_measure (child, GTK_ORIENTATION_VERTICAL, -1, 0, &marker_height, NULL, NULL);
256
257 ✗ shumate_viewport_location_to_widget_coords (viewport, widget, lat, lon, &x, &y);
258 ✗ calculate_local_marker_offset (SHUMATE_MARKER (child), marker_width, marker_height, &x, &y);
259
260 ✗ allocation.x = x;
261 ✗ allocation.y = y;
262 ✗ allocation.width = marker_width;
263 ✗ allocation.height = marker_height;
264
265 ✗ within_viewport = x > -allocation.width && x <= width &&
266 ✗ y > -allocation.height && y <= height &&
267 ✗ allocation.width < width && allocation.height < height;
268
269 ✗ gtk_widget_set_child_visible (child, within_viewport);
270
271 ✗ if (within_viewport)
272 ✗ gtk_widget_size_allocate (child, &allocation, -1);
273 }
274 ✗ }
275
276 static void
277 ✗ shumate_marker_layer_get_property (GObject *object,
278 guint property_id,
279 GValue *value,
280 GParamSpec *pspec)
281 {
282 ✗ ShumateMarkerLayer *self = SHUMATE_MARKER_LAYER (object);
283
284 ✗ switch (property_id)
285 {
286 ✗ case PROP_SELECTION_MODE:
287 ✗ g_value_set_enum (value, self->mode);
288 ✗ break;
289
290 ✗ default:
291 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
292 }
293 ✗ }
294
295
296 static void
297 ✗ shumate_marker_layer_set_property (GObject *object,
298 guint property_id,
299 const GValue *value,
300 GParamSpec *pspec)
301 {
302 ✗ ShumateMarkerLayer *self = SHUMATE_MARKER_LAYER (object);
303
304 ✗ switch (property_id)
305 {
306 ✗ case PROP_SELECTION_MODE:
307 ✗ shumate_marker_layer_set_selection_mode (self, g_value_get_enum (value));
308 ✗ break;
309
310 ✗ default:
311 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
312 }
313 ✗ }
314
315
316 static void
317 1 shumate_marker_layer_dispose (GObject *object)
318 {
319 1 ShumateMarkerLayer *self = SHUMATE_MARKER_LAYER (object);
320 1 ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
321 1 GtkWidget *child;
322
323 1 g_signal_handlers_disconnect_by_data (viewport, self);
324
325
2/2
✓ Branch 7 → 5 taken 2 times.
✓ Branch 7 → 8 taken 1 time.
3 while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
326 2 gtk_widget_unparent (child);
327
328 1 G_OBJECT_CLASS (shumate_marker_layer_parent_class)->dispose (object);
329 1 }
330
331
332 static void
333 1 shumate_marker_layer_finalize (GObject *object)
334 {
335 1 ShumateMarkerLayer *self = SHUMATE_MARKER_LAYER (object);
336
337 1 g_list_free (self->selected);
338
339 1 G_OBJECT_CLASS (shumate_marker_layer_parent_class)->finalize (object);
340 1 }
341
342 static void
343 5 shumate_marker_layer_constructed (GObject *object)
344 {
345 5 ShumateMarkerLayer *self = SHUMATE_MARKER_LAYER (object);
346 5 ShumateViewport *viewport;
347
348 5 G_OBJECT_CLASS (shumate_marker_layer_parent_class)->constructed (object);
349
350 5 viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
351 5 g_signal_connect_swapped (viewport, "notify", G_CALLBACK (on_viewport_changed), self);
352 5 }
353
354 static char *
355 ✗ shumate_marker_layer_get_debug_text (ShumateLayer *layer)
356 {
357 ✗ ShumateMarkerLayer *self = SHUMATE_MARKER_LAYER (layer);
358 ✗ return g_strdup_printf ("markers: %d, %d selected\n", self->n_children, g_list_length (self->selected));
359 }
360
361 static void
362 2 shumate_marker_layer_class_init (ShumateMarkerLayerClass *klass)
363 {
364 2 GObjectClass *object_class = G_OBJECT_CLASS (klass);
365 2 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
366 2 ShumateLayerClass *layer_class = SHUMATE_LAYER_CLASS (klass);
367
368 2 object_class->dispose = shumate_marker_layer_dispose;
369 2 object_class->finalize = shumate_marker_layer_finalize;
370 2 object_class->get_property = shumate_marker_layer_get_property;
371 2 object_class->set_property = shumate_marker_layer_set_property;
372 2 object_class->constructed = shumate_marker_layer_constructed;
373
374 2 widget_class->size_allocate = shumate_marker_layer_size_allocate;
375
376 2 layer_class->get_debug_text = shumate_marker_layer_get_debug_text;
377
378 /**
379 * ShumateMarkerLayer:selection-mode:
380 *
381 * Determines the type of selection that will be performed.
382 */
383 4 obj_properties[PROP_SELECTION_MODE] =
384 2 g_param_spec_enum ("selection-mode", NULL, NULL,
385 GTK_TYPE_SELECTION_MODE,
386 GTK_SELECTION_NONE,
387 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
388
389 2 g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties);
390
391 /**
392 * ShumateMarkerLayer::marker-selected:
393 * @self: The marker layer emitting the signal
394 * @marker: The marker that was selected
395 *
396 * Emitted when a marker in the layer is selected.
397 */
398 4 signals[MARKER_SELECTED] =
399 2 g_signal_new ("marker-selected",
400 G_OBJECT_CLASS_TYPE (object_class),
401 G_SIGNAL_RUN_LAST,
402 0, NULL, NULL, NULL,
403 G_TYPE_NONE,
404 1, SHUMATE_TYPE_MARKER);
405
406 /**
407 * ShumateMarkerLayer::marker-unselected:
408 * @self: The marker layer emitting the signal
409 * @marker: The marker that was unselected
410 *
411 * Emitted when a marker in the layer is unselected.
412 */
413 4 signals[MARKER_UNSELECTED] =
414 2 g_signal_new ("marker-unselected",
415 G_OBJECT_CLASS_TYPE (object_class),
416 G_SIGNAL_RUN_LAST,
417 0, NULL, NULL, NULL,
418 G_TYPE_NONE,
419 1, SHUMATE_TYPE_MARKER);
420 2 }
421
422
423 static void
424 5 shumate_marker_layer_init (ShumateMarkerLayer *self)
425 {
426 5 GtkGesture *click_gesture;
427
428 5 self->mode = GTK_SELECTION_NONE;
429
430 5 click_gesture = gtk_gesture_click_new ();
431 5 gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (click_gesture));
432 5 g_signal_connect_swapped (click_gesture, "released", G_CALLBACK (on_click_gesture_released), self);
433 5 }
434
435 /**
436 * shumate_marker_layer_new:
437 * @viewport: the @ShumateViewport
438 *
439 * Creates a new instance of [class@MarkerLayer].
440 *
441 * Returns: a new [class@MarkerLayer] ready to be used as a container for the markers.
442 */
443 ShumateMarkerLayer *
444 5 shumate_marker_layer_new (ShumateViewport *viewport)
445 {
446 5 return g_object_new (SHUMATE_TYPE_MARKER_LAYER,
447 "viewport", viewport,
448 NULL);
449 }
450
451
452 /**
453 * shumate_marker_layer_new_full:
454 * @viewport: the @ShumateViewport
455 * @mode: Selection mode
456 *
457 * Creates a new instance of [class@MarkerLayer] with the specified selection mode.
458 *
459 * Returns: a new [class@MarkerLayer] ready to be used as a container for the markers.
460 */
461 ShumateMarkerLayer *
462 ✗ shumate_marker_layer_new_full (ShumateViewport *viewport,
463 GtkSelectionMode mode)
464 {
465 ✗ return g_object_new (SHUMATE_TYPE_MARKER_LAYER,
466 "selection-mode", mode,
467 "viewport", viewport,
468 NULL);
469 }
470
471
472 static void
473 ✗ marker_position_notify (ShumateMarker *marker,
474 G_GNUC_UNUSED GParamSpec *pspec,
475 ShumateMarkerLayer *layer)
476 {
477 ✗ update_marker_visibility (layer, marker);
478 ✗ }
479
480
481 static void
482 ✗ marker_move_by_cb (ShumateMarker *marker,
483 double dx,
484 double dy,
485 GdkEvent *event,
486 ShumateMarkerLayer *layer)
487 {
488 /*ShumateMarkerLayerPrivate *priv = shumate_marker_layer_get_instance_private (layer);
489 ShumateView *view = self->view;
490 double x, y, lat, lon;
491
492 x = shumate_view_longitude_to_x (view, shumate_location_get_longitude (SHUMATE_LOCATION (marker)));
493 y = shumate_view_latitude_to_y (view, shumate_location_get_latitude (SHUMATE_LOCATION (marker)));
494
495 x += dx;
496 y += dy;
497
498 lon = shumate_view_x_to_longitude (view, x);
499 lat = shumate_view_y_to_latitude (view, y);
500
501 shumate_location_set_location (SHUMATE_LOCATION (marker), lat, lon);*/
502 ✗ }
503
504
505 /**
506 * shumate_marker_layer_add_marker:
507 * @self: a [class@MarkerLayer]
508 * @marker: a [class@Marker]
509 *
510 * Adds the marker to the layer.
511 */
512 void
513 104 shumate_marker_layer_add_marker (ShumateMarkerLayer *self,
514 ShumateMarker *marker)
515 {
516
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 104 times.
104 g_return_if_fail (SHUMATE_IS_MARKER_LAYER (self));
517
1/2
✓ Branch 6 → 7 taken 104 times.
✗ Branch 6 → 17 not taken.
104 g_return_if_fail (SHUMATE_IS_MARKER (marker));
518
519 104 g_signal_connect_object (G_OBJECT (marker), "notify::latitude",
520 G_CALLBACK (marker_position_notify), self, 0);
521 104 g_signal_connect_object (G_OBJECT (marker), "notify::longitude",
522 G_CALLBACK (marker_position_notify), self, 0);
523 104 g_signal_connect_object (G_OBJECT (marker), "notify::x-hotspot",
524 G_CALLBACK (marker_position_notify), self, 0);
525 104 g_signal_connect_object (G_OBJECT (marker), "notify::y-hotspot",
526 G_CALLBACK (marker_position_notify), self, 0);
527 104 g_signal_connect_object (G_OBJECT (marker), "notify::halign",
528 G_CALLBACK (marker_position_notify), self, 0);
529 104 g_signal_connect_object (G_OBJECT (marker), "notify::valign",
530 G_CALLBACK (marker_position_notify), self, 0);
531
532 /*g_signal_connect (G_OBJECT (marker), "drag-motion",
533 G_CALLBACK (marker_move_by_cb), layer);*/
534
535 104 shumate_marker_set_selected (marker, FALSE);
536
537 104 gtk_widget_insert_before (GTK_WIDGET(marker), GTK_WIDGET (self), NULL);
538 104 update_marker_visibility (self, marker);
539 104 self->n_children++;
540 }
541
542
543 /**
544 * shumate_marker_layer_remove_all:
545 * @self: a [class@MarkerLayer]
546 *
547 * Removes all markers from the layer.
548 */
549 void
550 1 shumate_marker_layer_remove_all (ShumateMarkerLayer *self)
551 {
552 1 GtkWidget *child;
553
554
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 g_return_if_fail (SHUMATE_IS_MARKER_LAYER (self));
555
556 1 child = gtk_widget_get_first_child (GTK_WIDGET (self));
557
2/2
✓ Branch 13 → 7 taken 100 times.
✓ Branch 13 → 14 taken 1 time.
101 while (child)
558 {
559 100 GtkWidget *next = gtk_widget_get_next_sibling (child);
560
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 100 times.
100 if (shumate_marker_is_selected (SHUMATE_MARKER (child))) {
561 ✗ shumate_marker_layer_unselect_marker (self, SHUMATE_MARKER (child));
562 }
563
564 100 g_signal_handlers_disconnect_by_data (child, self);
565 100 gtk_widget_unparent (child);
566
567 100 child = next;
568 }
569
570 1 self->n_children = 0;
571 }
572
573
574 /**
575 * shumate_marker_layer_get_markers:
576 * @self: a [class@MarkerLayer]
577 *
578 * Gets a copy of the list of all markers inserted into the layer. You should
579 * free the list but not its contents.
580 *
581 * Returns: (transfer container) (element-type ShumateMarker): the list
582 */
583 GList *
584 1 shumate_marker_layer_get_markers (ShumateMarkerLayer *self)
585 {
586 1 GList *list = NULL;
587 1 GtkWidget *child;
588
589
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 g_return_val_if_fail (SHUMATE_IS_MARKER_LAYER (self), NULL);
590
591 1 for (child = gtk_widget_get_last_child (GTK_WIDGET (self));
592
2/2
✓ Branch 8 → 6 taken 2 times.
✓ Branch 8 → 9 taken 1 time.
3 child != NULL;
593 2 child = gtk_widget_get_prev_sibling (child))
594 {
595 2 ShumateMarker *marker = SHUMATE_MARKER (child);
596 2 list = g_list_prepend (list, marker);
597 }
598
599 return list;
600 }
601
602
603 /**
604 * shumate_marker_layer_get_selected:
605 * @self: a [class@MarkerLayer]
606 *
607 * Gets a list of selected markers in the layer.
608 *
609 * Returns: (transfer container) (element-type ShumateMarker): the list
610 */
611 GList *
612 3 shumate_marker_layer_get_selected (ShumateMarkerLayer *self)
613 {
614
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3 times.
3 g_return_val_if_fail (SHUMATE_IS_MARKER_LAYER (self), FALSE);
615 3 return g_list_copy (self->selected);
616 }
617
618 static void
619 10 update_debug_text (ShumateMarkerLayer *self)
620 {
621
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 10 times.
10 if (shumate_inspector_settings_get_show_debug_overlay (shumate_inspector_settings_get_default ()))
622 ✗ gtk_widget_queue_draw (GTK_WIDGET (self));
623 10 }
624
625 /**
626 * shumate_marker_layer_select_marker:
627 * @self: a [class@MarkerLayer]
628 * @marker: a [class@Marker] that is a child of @self
629 *
630 * Selects a marker in this layer.
631 *
632 * If [class@MarkerLayer]:selection-mode is %GTK_SELECTION_SINGLE or
633 * %GTK_SELECTION_BROWSE, all other markers will be unselected. If the mode is
634 * %GTK_SELECTION_NONE or @marker is not selectable, nothing will happen.
635 *
636 * Returns: %TRUE if the marker is now selected, otherwise %FALSE
637 */
638 gboolean
639 7 shumate_marker_layer_select_marker (ShumateMarkerLayer *self, ShumateMarker *marker)
640 {
641
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 7 times.
7 g_return_val_if_fail (SHUMATE_IS_MARKER_LAYER (self), FALSE);
642
1/2
✓ Branch 6 → 7 taken 7 times.
✗ Branch 6 → 9 not taken.
7 g_return_val_if_fail (SHUMATE_IS_MARKER (marker), FALSE);
643
1/2
✓ Branch 8 → 10 taken 7 times.
✗ Branch 8 → 12 not taken.
7 g_return_val_if_fail (gtk_widget_get_parent (GTK_WIDGET (marker)) == GTK_WIDGET (self), FALSE);
644
645
1/2
✓ Branch 11 → 13 taken 7 times.
✗ Branch 11 → 23 not taken.
7 if (!shumate_marker_get_selectable (marker)) {
646 return FALSE;
647 }
648
649
1/2
✓ Branch 14 → 15 taken 7 times.
✗ Branch 14 → 23 not taken.
7 if (shumate_marker_is_selected (marker)) {
650 return TRUE;
651 }
652
653
3/4
✓ Branch 15 → 16 taken 2 times.
✓ Branch 15 → 17 taken 3 times.
✗ Branch 15 → 21 not taken.
✓ Branch 15 → 23 taken 2 times.
7 switch (self->mode) {
654 case GTK_SELECTION_NONE:
655 return FALSE;
656
657 2 case GTK_SELECTION_BROWSE:
658 case GTK_SELECTION_SINGLE:
659 2 shumate_marker_layer_unselect_all_markers (self);
660
661 /* fall through */
662 5 case GTK_SELECTION_MULTIPLE:
663 5 self->selected = g_list_prepend (self->selected, marker);
664 5 shumate_marker_set_selected (marker, TRUE);
665 5 update_debug_text (self);
666 5 g_signal_emit (self, signals[MARKER_SELECTED], 0, marker);
667 5 return TRUE;
668
669 ✗ default:
670 ✗ g_assert_not_reached ();
671 }
672 }
673
674
675 /**
676 * shumate_marker_layer_unselect_marker:
677 * @self: a [class@MarkerLayer]
678 * @marker: a [class@Marker] that is a child of @self
679 *
680 * Unselects a marker in this layer.
681 *
682 * This works even if [class@MarkerLayer]:selection-mode is
683 * %GTK_SELECTION_BROWSE. Browse mode only prevents user interaction, not the
684 * program, from unselecting a marker.
685 */
686 void
687 5 shumate_marker_layer_unselect_marker (ShumateMarkerLayer *self,
688 ShumateMarker *marker)
689 {
690
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
5 g_return_if_fail (SHUMATE_IS_MARKER_LAYER (self));
691
1/2
✓ Branch 6 → 7 taken 5 times.
✗ Branch 6 → 9 not taken.
5 g_return_if_fail (SHUMATE_IS_MARKER (marker));
692
1/2
✓ Branch 8 → 10 taken 5 times.
✗ Branch 8 → 12 not taken.
5 g_return_if_fail (gtk_widget_get_parent (GTK_WIDGET (marker)) == GTK_WIDGET (self));
693
694
1/2
✓ Branch 11 → 13 taken 5 times.
✗ Branch 11 → 17 not taken.
5 if (!shumate_marker_is_selected (marker)) {
695 return;
696 }
697
698 5 self->selected = g_list_remove (self->selected, marker);
699 5 shumate_marker_set_selected (marker, FALSE);
700 5 update_debug_text (self);
701 5 g_signal_emit (self, signals[MARKER_UNSELECTED], 0, marker);
702 }
703
704
705 /**
706 * shumate_marker_layer_remove_marker:
707 * @self: a [class@MarkerLayer]
708 * @marker: a [class@Marker]
709 *
710 * Removes the marker from the layer.
711 */
712 void
713 1 shumate_marker_layer_remove_marker (ShumateMarkerLayer *self,
714 ShumateMarker *marker)
715 {
716
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 g_return_if_fail (SHUMATE_IS_MARKER_LAYER (self));
717
1/2
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 9 not taken.
1 g_return_if_fail (SHUMATE_IS_MARKER (marker));
718
1/2
✓ Branch 8 → 10 taken 1 time.
✗ Branch 8 → 14 not taken.
1 g_return_if_fail (gtk_widget_get_parent (GTK_WIDGET (marker)) == GTK_WIDGET (self));
719
720 1 g_signal_handlers_disconnect_by_func (G_OBJECT (marker),
721 G_CALLBACK (marker_position_notify), self);
722
723 1 g_signal_handlers_disconnect_by_func (marker,
724 G_CALLBACK (marker_move_by_cb), self);
725
726
1/2
✗ Branch 13 → 15 not taken.
✓ Branch 13 → 16 taken 1 time.
1 if (shumate_marker_is_selected (marker)) {
727 ✗ shumate_marker_layer_unselect_marker (self, marker);
728 }
729
730 1 gtk_widget_unparent (GTK_WIDGET (marker));
731 1 self->n_children--;
732 }
733
734
735 /**
736 * shumate_marker_layer_unselect_all_markers:
737 * @self: a [class@MarkerLayer]
738 *
739 * Unselects all markers in the layer.
740 */
741 void
742 5 shumate_marker_layer_unselect_all_markers (ShumateMarkerLayer *self)
743 {
744 ✗ g_autoptr(GList) prev_selected = NULL;
745
746
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
5 g_return_if_fail (SHUMATE_IS_MARKER_LAYER (self));
747
748 5 prev_selected = g_list_copy (self->selected);
749
750
2/2
✓ Branch 9 → 7 taken 5 times.
✓ Branch 9 → 10 taken 5 times.
15 for (GList *l = prev_selected; l != NULL; l = l->next) {
751 5 shumate_marker_layer_unselect_marker (self, SHUMATE_MARKER (l->data));
752 }
753 }
754
755
756 /**
757 * shumate_marker_layer_select_all_markers:
758 * @self: a [class@MarkerLayer]
759 *
760 * Selects all selectable markers in the layer.
761 */
762 void
763 1 shumate_marker_layer_select_all_markers (ShumateMarkerLayer *self)
764 {
765 ✗ g_autoptr(GList) children = NULL;
766
767
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 g_return_if_fail (SHUMATE_IS_MARKER_LAYER (self));
768
769 1 children = shumate_marker_layer_get_markers (self);
770
771
2/2
✓ Branch 9 → 7 taken 2 times.
✓ Branch 9 → 10 taken 1 time.
4 for (GList *l = children; l != NULL; l = l->next) {
772 2 shumate_marker_layer_select_marker (self, SHUMATE_MARKER (l->data));
773 }
774 }
775
776
777 /**
778 * shumate_marker_layer_set_selection_mode:
779 * @self: a [class@MarkerLayer]
780 * @mode: a [enum@Gtk.SelectionMode] value
781 *
782 * Sets the selection mode of the layer.
783 *
784 * NOTE: changing selection mode to %GTK_SELECTION_NONE, %GTK_SELECTION_SINGLE
785 * or %GTK_SELECTION_BROWSE will clear all previously selected markers.
786 */
787 void
788 4 shumate_marker_layer_set_selection_mode (ShumateMarkerLayer *self,
789 GtkSelectionMode mode)
790 {
791
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
4 g_return_if_fail (SHUMATE_IS_MARKER_LAYER (self));
792
793
1/2
✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 9 not taken.
4 if (self->mode == mode)
794 return;
795
796 4 self->mode = mode;
797
798
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 8 taken 2 times.
4 if (mode != GTK_SELECTION_MULTIPLE)
799 2 shumate_marker_layer_unselect_all_markers (self);
800
801 4 g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_SELECTION_MODE]);
802 }
803
804
805 /**
806 * shumate_marker_layer_get_selection_mode:
807 * @self: a [class@MarkerLayer]
808 *
809 * Gets the selection mode of the layer.
810 *
811 * Returns: the selection mode of the layer.
812 */
813 GtkSelectionMode
814 1 shumate_marker_layer_get_selection_mode (ShumateMarkerLayer *self)
815 {
816
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 g_return_val_if_fail (SHUMATE_IS_MARKER_LAYER (self), GTK_SELECTION_NONE);
817
818 1 return self->mode;
819 }
820