GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 11.7% 22 / 0 / 188
Functions: 22.2% 4 / 0 / 18
Branches: 5.8% 5 / 0 / 86

shumate/shumate-scale.c
Line Branch Exec Source
1 /*
2 * Copyright (C) 2011-2013 Jiri Techet <techet@gmail.com>
3 * Copyright (C) 2019 Marcus Lundblad <ml@update.uu.se>
4 * Copyright (C) 2020 Collabora Ltd. (https://www.collabora.com)
5 * Copyright (C) 2020 Corentin Noël <corentin.noel@collabora.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * ShumateScale:
24 *
25 * A widget displaying a scale.
26 *
27 * # CSS nodes
28 *
29 * ```
30 * map-scale
31 * ├── label[.metric][.imperial]
32 * ```
33 *
34 * `ShumateScale` uses a single CSS node with name map-scale, it has up to two
35 * childs different labels.
36 */
37
38 #include "shumate-scale.h"
39 #include "shumate-enum-types.h"
40
41 #include <glib-object.h>
42 #include <glib/gi18n.h>
43 #include <math.h>
44 #include <string.h>
45
46 enum
47 {
48 PROP_UNIT = 1,
49 PROP_MAX_SCALE_WIDTH,
50 PROP_VIEWPORT,
51 N_PROPERTIES,
52 };
53
54 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
55
56 struct _ShumateScale
57 {
58 GtkWidget parent_instance;
59
60 ShumateUnit unit;
61 guint max_scale_width;
62
63 ShumateViewport *viewport;
64
65 GtkWidget *metric_label;
66 GtkWidget *imperial_label;
67 };
68
69
4/6
shumate_scale_class_intern_init:
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
shumate_scale_get_type:
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 7 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 7 not taken.
4 G_DEFINE_TYPE (ShumateScale, shumate_scale, GTK_TYPE_WIDGET);
70
71 #define FEET_IN_METERS 3.280839895
72
73 #define FEET_IN_A_MILE 5280.0
74
75 /*
76 * shumate_scale_compute_length:
77 * @self: a #ShumateScale
78 *
79 * This loop will find the pretty value to display on the scale.
80 * It will be run once for metric units, and twice for imperials
81 * so that both feet and miles have pretty numbers.
82 */
83 static gboolean
84 ✗ shumate_scale_compute_length (ShumateScale *self,
85 ShumateUnit unit,
86 float *out_scale_width,
87 float *out_base,
88 gboolean *out_is_small_unit)
89 {
90 ✗ ShumateMapSource *map_source;
91 ✗ double zoom_level;
92 ✗ double lat, lon;
93 ✗ float scale_width;
94 ✗ float base;
95 ✗ float factor;
96 ✗ gboolean is_small_unit = TRUE;
97 ✗ double m_per_pixel;
98
99 ✗ g_assert (SHUMATE_IS_SCALE (self));
100
101 ✗ if (out_scale_width)
102 ✗ *out_scale_width = 0;
103
104 ✗ if (out_base)
105 ✗ *out_base = 1;
106
107 ✗ if (out_is_small_unit)
108 ✗ *out_is_small_unit = TRUE;
109
110 ✗ if (!self->viewport)
111 return FALSE;
112
113 ✗ scale_width = self->max_scale_width;
114 ✗ zoom_level = shumate_viewport_get_zoom_level (self->viewport);
115 ✗ map_source = shumate_viewport_get_reference_map_source (self->viewport);
116 ✗ if (!map_source)
117 return FALSE;
118
119 ✗ lat = shumate_location_get_latitude (SHUMATE_LOCATION (self->viewport));
120 ✗ lon = shumate_location_get_longitude (SHUMATE_LOCATION (self->viewport));
121 ✗ m_per_pixel = shumate_map_source_get_meters_per_pixel (map_source,
122 zoom_level,
123 lat,
124 lon);
125
126 ✗ if (unit == SHUMATE_UNIT_IMPERIAL)
127 ✗ m_per_pixel *= FEET_IN_METERS; /* m_per_pixel is now in ft */
128
129 ✗ do
130 {
131 /* Keep the previous power of 10 */
132 ✗ base = floor (log (m_per_pixel * scale_width) / log (10));
133 ✗ base = pow (10, base);
134
135 /* How many times can it be fitted in our max scale width */
136 ✗ g_assert (base > 0);
137 ✗ g_assert (m_per_pixel * scale_width / base > 0);
138 ✗ scale_width /= m_per_pixel * scale_width / base;
139 ✗ g_assert (scale_width > 0);
140 ✗ factor = floor (self->max_scale_width / scale_width);
141 ✗ base *= factor;
142 ✗ scale_width *= factor;
143
144 ✗ if (unit == SHUMATE_UNIT_METRIC)
145 {
146 ✗ if (base / 1000.0 >= 1)
147 {
148 ✗ base /= 1000.0; /* base is now in km */
149 ✗ is_small_unit = FALSE;
150 }
151
152 break;
153 }
154 ✗ else if (unit == SHUMATE_UNIT_IMPERIAL)
155 {
156 ✗ if (is_small_unit && base / FEET_IN_A_MILE >= 1)
157 {
158 ✗ m_per_pixel /= FEET_IN_A_MILE; /* m_per_pixel is now in miles */
159 ✗ is_small_unit = FALSE;
160 /* we need to recompute the base because 1000 ft != 1 mile */
161 }
162 else
163 break;
164 }
165 } while (TRUE);
166
167
168 ✗ if (out_scale_width)
169 ✗ *out_scale_width = scale_width;
170
171 ✗ if (out_base)
172 ✗ *out_base = base;
173
174 ✗ if (out_is_small_unit)
175 ✗ *out_is_small_unit = is_small_unit;
176
177 return TRUE;
178 }
179
180 static void
181 ✗ shumate_scale_on_scale_changed (ShumateScale *self)
182 {
183 ✗ float metric_scale_width, metric_base;
184 ✗ float imperial_scale_width, imperial_base;
185 ✗ gboolean metric_is_small_unit, imperial_is_small_unit;
186 ✗ g_autofree char *metric_label = NULL;
187 ✗ g_autofree char *imperial_label = NULL;
188
189 ✗ shumate_scale_compute_length (self, SHUMATE_UNIT_METRIC, &metric_scale_width, &metric_base, &metric_is_small_unit);
190 ✗ shumate_scale_compute_length (self, SHUMATE_UNIT_IMPERIAL, &imperial_scale_width, &imperial_base, &imperial_is_small_unit);
191
192 ✗ gtk_widget_set_size_request (self->metric_label, metric_scale_width, -1);
193 ✗ gtk_widget_set_size_request (self->imperial_label, imperial_scale_width, -1);
194
195 ✗ if (metric_is_small_unit)
196 // m is the unit for meters
197 ✗ metric_label = g_strdup_printf (_("%d m"), (int) metric_base);
198 else
199 // km is the unit for kilometers
200 ✗ metric_label = g_strdup_printf (_("%d km"), (int) metric_base);
201
202 ✗ gtk_label_set_label (GTK_LABEL (self->metric_label), metric_label);
203
204 ✗ if (imperial_is_small_unit)
205 // ft is the unit for feet
206 ✗ imperial_label = g_strdup_printf (_("%d ft"), (int) imperial_base);
207 else
208 // mi is the unit for miles
209 ✗ imperial_label = g_strdup_printf (_("%d mi"), (int) imperial_base);
210
211 ✗ gtk_label_set_label (GTK_LABEL (self->imperial_label), imperial_label);
212 ✗ gtk_widget_queue_resize (GTK_WIDGET (self));
213 ✗ }
214
215 static void
216 ✗ on_viewport_props_changed (ShumateScale *self,
217 G_GNUC_UNUSED GParamSpec *pspec,
218 ShumateViewport *viewport)
219 {
220 ✗ shumate_scale_on_scale_changed (self);
221 ✗ }
222
223
224 static void
225 ✗ shumate_scale_get_property (GObject *object,
226 guint prop_id,
227 GValue *value,
228 GParamSpec *pspec)
229 {
230 ✗ ShumateScale *scale = SHUMATE_SCALE (object);
231
232 ✗ switch (prop_id)
233 {
234 ✗ case PROP_UNIT:
235 ✗ g_value_set_enum (value, scale->unit);
236 ✗ break;
237
238 ✗ case PROP_MAX_SCALE_WIDTH:
239 ✗ g_value_set_uint (value, scale->max_scale_width);
240 ✗ break;
241
242 ✗ case PROP_VIEWPORT:
243 ✗ g_value_set_object (value, scale->viewport);
244 ✗ break;
245
246 ✗ default:
247 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
248 }
249 ✗ }
250
251
252 static void
253 ✗ shumate_scale_set_property (GObject *object,
254 guint prop_id,
255 const GValue *value,
256 GParamSpec *pspec)
257 {
258 ✗ ShumateScale *scale = SHUMATE_SCALE (object);
259
260 ✗ switch (prop_id)
261 {
262 ✗ case PROP_UNIT:
263 ✗ shumate_scale_set_unit (scale, g_value_get_enum (value));
264 ✗ break;
265
266 ✗ case PROP_MAX_SCALE_WIDTH:
267 ✗ shumate_scale_set_max_width (scale, g_value_get_uint (value));
268 ✗ break;
269
270 ✗ case PROP_VIEWPORT:
271 ✗ shumate_scale_set_viewport (scale, g_value_get_object (value));
272 ✗ break;
273
274 ✗ default:
275 ✗ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
276 }
277 ✗ }
278
279 static void
280 ✗ shumate_scale_dispose (GObject *object)
281 {
282 ✗ ShumateScale *scale = SHUMATE_SCALE (object);
283
284 ✗ g_signal_handlers_disconnect_by_data (scale->viewport, scale);
285 ✗ g_clear_object (&scale->viewport);
286 ✗ g_clear_pointer (&scale->metric_label, gtk_widget_unparent);
287 ✗ g_clear_pointer (&scale->imperial_label, gtk_widget_unparent);
288
289 ✗ G_OBJECT_CLASS (shumate_scale_parent_class)->dispose (object);
290 ✗ }
291
292 static void
293 1 shumate_scale_class_init (ShumateScaleClass *klass)
294 {
295 1 GObjectClass *object_class = G_OBJECT_CLASS (klass);
296 1 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
297 1 GdkDisplay *display;
298
299 1 object_class->dispose = shumate_scale_dispose;
300 1 object_class->get_property = shumate_scale_get_property;
301 1 object_class->set_property = shumate_scale_set_property;
302
303 /**
304 * ShumateScale:max-width:
305 *
306 * The size of the map scale on screen in pixels.
307 */
308 2 obj_properties[PROP_MAX_SCALE_WIDTH] =
309 1 g_param_spec_uint ("max-width", NULL, NULL,
310 1, G_MAXUINT, 150,
311 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
312
313 /**
314 * ShumateScale:unit:
315 *
316 * The scale's units.
317 */
318 2 obj_properties[PROP_UNIT] =
319 1 g_param_spec_enum ("unit", NULL, NULL,
320 SHUMATE_TYPE_UNIT,
321 SHUMATE_UNIT_BOTH,
322 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
323
324 /**
325 * ShumateScale:viewport:
326 *
327 * The viewport to use.
328 */
329 2 obj_properties[PROP_VIEWPORT] =
330 1 g_param_spec_object ("viewport", NULL, NULL,
331 SHUMATE_TYPE_VIEWPORT,
332 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
333
334 1 g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties);
335
336 1 gtk_widget_class_set_css_name (widget_class, "map-scale");
337 1 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
338
339 1 display = gdk_display_get_default ();
340
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 18 taken 1 time.
1 if (display)
341 {
342 ✗ g_autoptr(GtkCssProvider) provider = gtk_css_provider_new ();
343 ✗ gtk_css_provider_load_from_resource (provider, "/org/gnome/shumate/scale.css");
344 ✗ gtk_style_context_add_provider_for_display (display,
345 GTK_STYLE_PROVIDER (provider),
346 GTK_STYLE_PROVIDER_PRIORITY_FALLBACK);
347 }
348 1 }
349
350 static void
351 ✗ shumate_scale_init (ShumateScale *self)
352 {
353 ✗ GtkWidget *self_widget = GTK_WIDGET (self);
354 ✗ GtkLayoutManager *layout = gtk_widget_get_layout_manager (self_widget);
355
356 ✗ self->unit = SHUMATE_UNIT_BOTH;
357 ✗ self->max_scale_width = 150;
358
359 ✗ gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), GTK_ORIENTATION_VERTICAL);
360 ✗ gtk_widget_add_css_class (self_widget, "vertical");
361
362 ✗ self->metric_label = gtk_label_new (NULL);
363 ✗ g_object_set (G_OBJECT (self->metric_label),
364 "xalign", 0.0f,
365 "halign", GTK_ALIGN_START,
366 "ellipsize", PANGO_ELLIPSIZE_END,
367 NULL);
368 ✗ gtk_widget_add_css_class (self->metric_label, "metric");
369 ✗ self->imperial_label = gtk_label_new (NULL);
370 ✗ g_object_set (G_OBJECT (self->imperial_label),
371 "xalign", 0.0f,
372 "halign", GTK_ALIGN_START,
373 "ellipsize", PANGO_ELLIPSIZE_END,
374 NULL);
375 ✗ gtk_widget_add_css_class (self->imperial_label, "imperial");
376
377 ✗ gtk_widget_insert_after (self->metric_label, self_widget, NULL);
378 ✗ gtk_widget_insert_after (self->imperial_label, self_widget, self->metric_label);
379 ✗ }
380
381
382 /**
383 * shumate_scale_new:
384 * @viewport: (nullable): a #ShumateViewport
385 *
386 * Creates an instance of #ShumateScale.
387 *
388 * Returns: a new #ShumateScale.
389 */
390 ShumateScale *
391 ✗ shumate_scale_new (ShumateViewport *viewport)
392 {
393 ✗ return SHUMATE_SCALE (g_object_new (SHUMATE_TYPE_SCALE,
394 "viewport", viewport,
395 NULL));
396 }
397
398
399 /**
400 * shumate_scale_set_max_width:
401 * @scale: a #ShumateScale
402 * @value: the number of pixels
403 *
404 * Sets the maximum width of the scale on the screen in pixels
405 */
406 void
407 ✗ shumate_scale_set_max_width (ShumateScale *scale,
408 guint value)
409 {
410 ✗ g_return_if_fail (SHUMATE_IS_SCALE (scale));
411
412 ✗ if (scale->max_scale_width == value)
413 return;
414
415 ✗ scale->max_scale_width = value;
416 ✗ g_object_notify_by_pspec(G_OBJECT (scale), obj_properties[PROP_MAX_SCALE_WIDTH]);
417 ✗ shumate_scale_on_scale_changed (scale);
418 }
419
420
421 /**
422 * shumate_scale_set_unit:
423 * @scale: a #ShumateScale
424 * @unit: a #ShumateUnit
425 *
426 * Sets the scale unit.
427 */
428 void
429 ✗ shumate_scale_set_unit (ShumateScale *scale,
430 ShumateUnit unit)
431 {
432 ✗ g_return_if_fail (SHUMATE_IS_SCALE (scale));
433
434 ✗ if (scale->unit == unit)
435 return;
436
437 ✗ scale->unit = unit;
438
439 ✗ gtk_widget_set_visible (scale->metric_label, unit == SHUMATE_UNIT_METRIC || unit == SHUMATE_UNIT_BOTH);
440 ✗ gtk_widget_set_visible (scale->imperial_label, unit == SHUMATE_UNIT_IMPERIAL || unit == SHUMATE_UNIT_BOTH);
441
442 ✗ g_object_notify_by_pspec(G_OBJECT (scale), obj_properties[PROP_UNIT]);
443 ✗ shumate_scale_on_scale_changed (scale);
444 }
445
446 /**
447 * shumate_scale_set_viewport:
448 * @scale: a #ShumateScale
449 * @viewport: (nullable): a #ShumateViewport
450 *
451 * Sets the scale viewport.
452 */
453 void
454 ✗ shumate_scale_set_viewport (ShumateScale *scale,
455 ShumateViewport *viewport)
456 {
457 ✗ g_return_if_fail (SHUMATE_IS_SCALE (scale));
458
459 ✗ if (scale->viewport)
460 ✗ g_signal_handlers_disconnect_by_data (scale->viewport, scale);
461
462 ✗ if (g_set_object (&scale->viewport, viewport))
463 {
464 ✗ g_object_notify_by_pspec(G_OBJECT (scale), obj_properties[PROP_VIEWPORT]);
465 ✗ if (scale->viewport)
466 {
467 ✗ g_signal_connect_swapped (scale->viewport, "notify::latitude", G_CALLBACK (on_viewport_props_changed), scale);
468 ✗ g_signal_connect_swapped (scale->viewport, "notify::zoom-level", G_CALLBACK (on_viewport_props_changed), scale);
469 ✗ g_signal_connect_swapped (scale->viewport, "notify::reference-map-source", G_CALLBACK (on_viewport_props_changed), scale);
470 }
471
472 ✗ shumate_scale_on_scale_changed (scale);
473 }
474 }
475
476
477 /**
478 * shumate_scale_get_max_width:
479 * @scale: a #ShumateScale
480 *
481 * Gets the maximum scale width.
482 *
483 * Returns: The maximum scale width in pixels.
484 */
485 guint
486 ✗ shumate_scale_get_max_width (ShumateScale *scale)
487 {
488 ✗ g_return_val_if_fail (SHUMATE_IS_SCALE (scale), FALSE);
489
490 ✗ return scale->max_scale_width;
491 }
492
493
494 /**
495 * shumate_scale_get_unit:
496 * @scale: a #ShumateScale
497 *
498 * Gets the unit used by the scale.
499 *
500 * Returns: The unit used by the scale
501 */
502 ShumateUnit
503 ✗ shumate_scale_get_unit (ShumateScale *scale)
504 {
505 ✗ g_return_val_if_fail (SHUMATE_IS_SCALE (scale), FALSE);
506
507 ✗ return scale->unit;
508 }
509
510 /**
511 * shumate_scale_get_viewport:
512 * @scale: a #ShumateScale
513 *
514 * Gets the viewport used by the scale.
515 *
516 * Returns: (transfer none) (nullable): The #ShumateViewport used by the scale
517 */
518 ShumateViewport *
519 ✗ shumate_scale_get_viewport (ShumateScale *scale)
520 {
521 ✗ g_return_val_if_fail (SHUMATE_IS_SCALE (scale), NULL);
522
523 ✗ return scale->viewport;
524 }
525