2 * Copyright (C) 2006, 2007 John Costigan.
4 * POI and GPS-Info code originally written by Cezary Jackiewicz.
6 * Default map data provided by http://www.openstreetmap.org/
8 * This file is part of Maemo Mapper.
10 * Maemo Mapper is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
15 * Maemo Mapper is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with Maemo Mapper. If not, see <http://www.gnu.org/licenses/>.
35 # include <hildon/hildon-help.h>
36 # include <hildon/hildon-note.h>
37 # include <hildon/hildon-file-chooser-dialog.h>
38 # include <hildon/hildon-banner.h>
39 # include <hildon/hildon-sound.h>
41 # include <osso-helplib.h>
42 # include <hildon-widgets/hildon-note.h>
43 # include <hildon-widgets/hildon-file-chooser-dialog.h>
44 # include <hildon-widgets/hildon-banner.h>
45 # include <hildon-widgets/hildon-system-sound.h>
54 #include "gdk-pixbuf-rotate.h"
62 #define VELVEC_SIZE_FACTOR (4)
64 static GtkWidget *_sat_details_panel = NULL;
65 static GtkWidget *_sdi_lat = NULL;
66 static GtkWidget *_sdi_lon = NULL;
67 static GtkWidget *_sdi_spd = NULL;
68 static GtkWidget *_sdi_alt = NULL;
69 static GtkWidget *_sdi_hea = NULL;
70 static GtkWidget *_sdi_tim = NULL;
71 static GtkWidget *_sdi_vie = NULL;
72 static GtkWidget *_sdi_use = NULL;
73 static GtkWidget *_sdi_fix = NULL;
74 static GtkWidget *_sdi_fqu = NULL;
75 static GtkWidget *_sdi_msp = NULL;
76 static gint _redraw_count = 0;
78 static gint _mark_bufx1 = -1;
79 static gint _mark_bufx2 = -1;
80 static gint _mark_bufy1 = -1;
81 static gint _mark_bufy2 = -1;
82 static gint _mark_minx = -1;
83 static gint _mark_miny = -1;
84 static gint _mark_width = -1;
85 static gint _mark_height = -1;
86 static GdkRectangle _scale_rect = { 0, 0, 0, 0};
87 static GdkRectangle _zoom_rect = { 0, 0, 0, 0};
88 static gint _dl_errors = 0;
90 static volatile gint _pending_replaces = 0;
93 GdkRectangle _comprose_rect = { 0, 0, 0, 0};
94 PangoLayout *_scale_layout = NULL;
95 PangoLayout *_zoom_layout = NULL;
96 PangoLayout *_comprose_layout = NULL;
97 GdkGC *_speed_limit_gc1 = NULL;
98 GdkGC *_speed_limit_gc2 = NULL;
99 PangoLayout *_speed_limit_layout = NULL;
100 PangoLayout *_sat_panel_layout = NULL;
101 PangoLayout *_heading_panel_layout = NULL;
102 PangoFontDescription *_heading_panel_fontdesc = NULL;
103 GdkGC *_sat_info_gc1 = NULL;
104 GdkGC *_sat_info_gc2 = NULL;
105 PangoLayout *_sat_info_layout = NULL;
106 PangoLayout *_sat_details_layout = NULL;
107 PangoLayout *_sat_details_expose_layout = NULL;
109 #define SCALE_WIDTH (100)
114 printf("%s()\n", __PRETTY_FUNCTION__);
118 hildon_play_system_sound(
119 "/usr/share/sounds/ui-information_note.wav");
121 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
131 static gint x = 0, y = 0, width = 0, height = 0;
132 printf("%s()\n", __PRETTY_FUNCTION__);
134 cur_speed = _gps.speed * UNITS_CONVERT[_units];
136 if(cur_speed > _speed_limit)
138 gc = _speed_limit_gc1;
141 _speed_excess = TRUE;
142 g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
143 5000, (GSourceFunc)speed_excess, NULL, NULL);
148 gc = _speed_limit_gc2;
149 _speed_excess = FALSE;
152 /* remove previous number */
153 if (width != 0 && height != 0) {
154 gtk_widget_queue_draw_area (_map_widget,
159 gdk_window_process_all_updates();
162 buffer = g_strdup_printf("%0.0f", cur_speed);
163 pango_layout_set_text(_speed_limit_layout, buffer, -1);
166 pango_layout_get_pixel_size(_speed_limit_layout, &width, &height);
168 switch (_speed_location)
170 case SPEED_LOCATION_TOP_RIGHT:
171 x = _map_widget->allocation.width - 10 - width;
174 case SPEED_LOCATION_BOTTOM_RIGHT:
175 x = _map_widget->allocation.width - 10 - width;
176 y = _map_widget->allocation.height - 10 - height;
178 case SPEED_LOCATION_BOTTOM_LEFT:
180 y = _map_widget->allocation.height - 10 - height;
188 gdk_draw_layout(_map_widget->window,
191 _speed_limit_layout);
194 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
198 gps_display_details(void)
200 gchar *buffer, litbuf[16];
201 printf("%s()\n", __PRETTY_FUNCTION__);
206 gtk_label_set_label(GTK_LABEL(_sdi_lat), " --- ");
207 gtk_label_set_label(GTK_LABEL(_sdi_lon), " --- ");
208 gtk_label_set_label(GTK_LABEL(_sdi_spd), " --- ");
209 gtk_label_set_label(GTK_LABEL(_sdi_alt), " --- ");
210 gtk_label_set_label(GTK_LABEL(_sdi_hea), " --- ");
211 gtk_label_set_label(GTK_LABEL(_sdi_tim), " --:--:-- ");
215 gfloat speed = _gps.speed * UNITS_CONVERT[_units];
218 lat_format(_gps.lat, litbuf);
219 gtk_label_set_label(GTK_LABEL(_sdi_lat), litbuf);
222 lon_format(_gps.lon, litbuf);
223 gtk_label_set_label(GTK_LABEL(_sdi_lon), litbuf);
229 buffer = g_strdup_printf("%.1f mph", speed);
232 buffer = g_strdup_printf("%.1f kn", speed);
235 buffer = g_strdup_printf("%.1f km/h", speed);
238 gtk_label_set_label(GTK_LABEL(_sdi_spd), buffer);
246 buffer = g_strdup_printf("%d ft",
247 (gint)(_pos.altitude * 3.2808399f));
250 buffer = g_strdup_printf("%d m", _pos.altitude);
253 gtk_label_set_label(GTK_LABEL(_sdi_alt), buffer);
257 buffer = g_strdup_printf("%0.0f°", _gps.heading);
258 gtk_label_set_label(GTK_LABEL(_sdi_hea), buffer);
262 strftime(litbuf, 15, "%X", localtime(&_pos.time));
263 gtk_label_set_label(GTK_LABEL(_sdi_tim), litbuf);
267 buffer = g_strdup_printf("%d", _gps.satinview);
268 gtk_label_set_label(GTK_LABEL(_sdi_vie), buffer);
272 buffer = g_strdup_printf("%d", _gps.satinuse);
273 gtk_label_set_label(GTK_LABEL(_sdi_use), buffer);
280 case 3: buffer = g_strdup_printf("%dD fix", _gps.fix); break;
281 default: buffer = g_strdup_printf("nofix"); break;
283 gtk_label_set_label(GTK_LABEL(_sdi_fix), buffer);
287 buffer = g_strdup("none");
290 switch (_gps.fixquality)
292 case 1 : buffer = g_strdup_printf(_("SPS")); break;
293 case 2 : buffer = g_strdup_printf(_("DGPS")); break;
294 case 3 : buffer = g_strdup_printf(_("PPS")); break;
295 case 4 : buffer = g_strdup_printf(_("Real Time Kinematic")); break;
296 case 5 : buffer = g_strdup_printf(_("Float RTK")); break;
297 case 6 : buffer = g_strdup_printf(_("Estimated")); break;
298 case 7 : buffer = g_strdup_printf(_("Manual")); break;
299 case 8 : buffer = g_strdup_printf(_("Simulation")); break;
300 default : buffer = g_strdup_printf(_("none")); break;
303 gtk_label_set_label(GTK_LABEL(_sdi_fqu), buffer);
308 gfloat maxspeed = _gps.maxspeed * UNITS_CONVERT[_units];
314 buffer = g_strdup_printf("%.1f mph", maxspeed);
317 buffer = g_strdup_printf("%.1f kn", maxspeed);
320 buffer = g_strdup_printf("%.1f km/h", maxspeed);
323 gtk_label_set_label(GTK_LABEL(_sdi_msp), buffer);
327 /* refresh sat panel */
328 gtk_widget_queue_draw_area(GTK_WIDGET(_sat_details_panel),
330 _sat_details_panel->allocation.width,
331 _sat_details_panel->allocation.height);
333 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
338 gps_display_data(void)
340 gchar *buffer, litbuf[LL_FMT_LEN];
341 printf("%s()\n", __PRETTY_FUNCTION__);
346 gtk_label_set_label(GTK_LABEL(_text_lat), " --- ");
347 gtk_label_set_label(GTK_LABEL(_text_lon), " --- ");
348 gtk_label_set_label(GTK_LABEL(_text_speed), " --- ");
349 gtk_label_set_label(GTK_LABEL(_text_alt), " --- ");
350 gtk_label_set_label(GTK_LABEL(_text_time), " --:--:-- ");
354 gfloat speed = _gps.speed * UNITS_CONVERT[_units];
357 lat_format(_gps.lat, litbuf);
358 gtk_label_set_label(GTK_LABEL(_text_lat), litbuf);
361 lon_format(_gps.lon, litbuf);
362 gtk_label_set_label(GTK_LABEL(_text_lon), litbuf);
368 buffer = g_strdup_printf("Spd: %.1f mph", speed);
371 buffer = g_strdup_printf("Spd: %.1f kn", speed);
374 buffer = g_strdup_printf("Spd: %.1f km/h", speed);
377 gtk_label_set_label(GTK_LABEL(_text_speed), buffer);
385 buffer = g_strdup_printf("Alt: %d ft",
386 (gint)(_pos.altitude * 3.2808399f));
389 buffer = g_strdup_printf("Alt: %d m", _pos.altitude);
391 gtk_label_set_label(GTK_LABEL(_text_alt), buffer);
395 strftime(litbuf, 15, "%X", localtime(&_pos.time));
396 gtk_label_set_label(GTK_LABEL(_text_time), litbuf);
399 /* refresh sat panel */
400 gtk_widget_queue_draw_area(GTK_WIDGET(_sat_panel),
402 _sat_panel->allocation.width,
403 _sat_panel->allocation.height);
405 /* refresh heading panel*/
406 gtk_widget_queue_draw_area(GTK_WIDGET(_heading_panel),
408 _heading_panel->allocation.width,
409 _heading_panel->allocation.height);
411 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
418 printf("%s()\n", __PRETTY_FUNCTION__);
428 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
434 printf("%s()\n", __PRETTY_FUNCTION__);
436 if(_gps_info && _enable_gps)
437 gtk_widget_show_all(GTK_WIDGET(_gps_widget));
441 gtk_widget_hide(GTK_WIDGET(_gps_widget));
444 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
448 draw_sat_info(GtkWidget *widget, gint x0, gint y0,
449 gint width, gint height, gboolean showsnr)
452 gint step, i, j, snr_height, bymargin, xoffset, yoffset;
455 printf("%s()\n", __PRETTY_FUNCTION__);
459 /* Bootom margin - 12% */
460 bymargin = height * 0.88f;
463 gdk_draw_line(widget->window,
464 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
465 xoffset + 5, yoffset + bymargin,
466 xoffset + width - 10 - 2, yoffset + bymargin);
467 gdk_draw_line(widget->window,
468 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
469 xoffset + 5, yoffset + bymargin - 1,
470 xoffset + width - 10 - 2, yoffset + bymargin - 1);
472 if(_gps.satinview > 0 )
474 /* Left margin - 5pix, Right margin - 5pix */
475 step = (width - 10) / _gps.satinview;
477 for(i = 0; i < _gps.satinview; i++)
479 /* Sat used or not */
481 for(j = 0; j < _gps.satinuse ; j++)
483 if(_gps.satforfix[j] == _gps_sat[i].prn)
491 snr_height = _gps_sat[i].snr * height * 0.78f / 100;
492 y = height * 0.1f + (height * 0.78f - snr_height);
494 /* draw sat rectangle... */
495 gdk_draw_rectangle(widget->window,
503 if(showsnr && _gps_sat[i].snr > 0)
506 tmp = g_strdup_printf("%02d", _gps_sat[i].snr);
507 pango_layout_set_text(_sat_info_layout, tmp, 2);
508 pango_layout_get_pixel_size(_sat_info_layout, &x1, &y1);
509 gdk_draw_layout(widget->window,
510 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
511 xoffset + x + ((step - 2) - x1)/2,
517 /* ...and sat number */
518 tmp = g_strdup_printf("%02d", _gps_sat[i].prn);
519 pango_layout_set_text(_sat_info_layout, tmp, 2);
520 pango_layout_get_pixel_size(_sat_info_layout, &x1, &y1);
521 gdk_draw_layout(widget->window,
522 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
523 xoffset + x + ((step - 2) - x1)/2 ,
524 yoffset + bymargin + 1,
530 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
535 draw_sat_details(GtkWidget *widget, gint x0, gint y0,
536 gint width, gint height)
538 gint i, j, x, y, size, halfsize, xoffset, yoffset;
542 GdkGC *gc1, *gc2, *gc3, *gc;
543 gchar *buffer = NULL;
544 printf("%s()\n", __PRETTY_FUNCTION__);
546 size = MIN(width, height);
550 xoffset = x0 + (width - height - 10) / 2;
556 yoffset = y0 + (height - width - 10) / 2;
560 gdk_draw_arc(widget->window,
561 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
563 xoffset + 2, yoffset + 2, size - 4, size - 4,
567 gdk_draw_arc(widget->window,
568 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
570 xoffset + size/6, yoffset + size/6,
575 gdk_draw_arc(widget->window,
576 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
578 xoffset + size/6*2, yoffset + size/6*2,
582 gint line[12] = {0,30,60,90,120,150,180,210,240,270,300,330};
584 for(i = 0; i < 6; i++)
587 tmp = deg2rad(line[i]);
588 gdk_draw_line(widget->window,
589 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
590 xoffset + halfsize + (halfsize -2) * sinf(tmp),
591 yoffset + halfsize - (halfsize -2) * cosf(tmp),
592 xoffset + halfsize - (halfsize -2) * sinf(tmp),
593 yoffset + halfsize + (halfsize -2) * cosf(tmp));
596 for(i = 0; i < 12; i++)
598 tmp = deg2rad(line[i]);
601 buffer = g_strdup_printf("N");
603 buffer = g_strdup_printf("%d°", line[i]);
604 pango_layout_set_text(_sat_details_layout, buffer, -1);
605 pango_layout_get_pixel_size(_sat_details_layout, &x, &y);
606 gdk_draw_layout(widget->window,
607 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
608 (xoffset + halfsize + (halfsize - size/12) * sinf(tmp)) - x/2,
609 (yoffset + halfsize - (halfsize - size/12) * cosf(tmp)) - y/2,
610 _sat_details_layout);
616 buffer = g_strdup_printf("30°");
617 pango_layout_set_text(_sat_details_layout, buffer, -1);
618 pango_layout_get_pixel_size(_sat_details_layout, &x, &y);
619 gdk_draw_layout(widget->window,
620 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
621 (xoffset + halfsize + size/6*2 * sinf(tmp)) - x/2,
622 (yoffset + halfsize - size/6*2 * cosf(tmp)) - y/2,
623 _sat_details_layout);
628 buffer = g_strdup_printf("60°");
629 pango_layout_set_text(_sat_details_layout, buffer, -1);
630 pango_layout_get_pixel_size(_sat_details_layout, &x, &y);
631 gdk_draw_layout(widget->window,
632 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
633 (xoffset + halfsize + size/6 * sinf(tmp)) - x/2,
634 (yoffset + halfsize - size/6 * cosf(tmp)) - y/2,
635 _sat_details_layout);
641 gc1 = gdk_gc_new (widget->window);
642 gdk_gc_set_rgb_fg_color (gc1, &color);
647 gc2 = gdk_gc_new (widget->window);
648 gdk_gc_set_rgb_fg_color (gc2, &color);
651 color.green = 0xffff;
653 gc3 = gdk_gc_new (widget->window);
654 gdk_gc_set_rgb_fg_color (gc3, &color);
656 for(i = 0; i < _gps.satinview; i++)
658 /* Sat used or not */
660 for(j = 0; j < _gps.satinuse ; j++)
662 if(_gps.satforfix[j] == _gps_sat[i].prn)
669 tmp = deg2rad(_gps_sat[i].azimuth);
670 x = xoffset + halfsize
671 + (90 - _gps_sat[i].elevation)*halfsize/90 * sinf(tmp);
672 y = yoffset + halfsize
673 - (90 - _gps_sat[i].elevation)*halfsize/90 * cosf(tmp);
675 gdk_draw_arc (widget->window,
677 x - 10, y - 10, 20, 20,
680 buffer = g_strdup_printf("%02d", _gps_sat[i].prn);
681 pango_layout_set_text(_sat_details_layout, buffer, -1);
682 pango_layout_get_pixel_size(_sat_details_layout, &x1, &y1);
683 gdk_draw_layout(widget->window,
687 _sat_details_layout);
690 g_object_unref (gc1);
691 g_object_unref (gc2);
692 g_object_unref (gc3);
694 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
700 sat_details_panel_expose(GtkWidget *widget, GdkEventExpose *event)
702 gint width, height, x, y;
703 gchar *buffer = NULL;
704 printf("%s()\n", __PRETTY_FUNCTION__);
706 width = widget->allocation.width;
707 height = widget->allocation.height * 0.9;
709 draw_sat_info(widget, 0, 0, width/2, height, TRUE);
710 draw_sat_details(widget, width/2, 0, width/2, height);
712 buffer = g_strdup_printf(
714 _("Satellites in view"), _gps.satinview,
715 _("in use"), _gps.satinuse);
716 pango_layout_set_text(_sat_details_expose_layout, buffer, -1);
717 pango_layout_get_pixel_size(_sat_details_expose_layout, &x, &y);
718 gdk_draw_layout(widget->window,
719 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
722 _sat_details_expose_layout);
725 buffer = g_strdup_printf("HDOP: %.01f", _gps.hdop);
726 pango_layout_set_text(_sat_details_expose_layout, buffer, -1);
727 pango_layout_get_pixel_size(_sat_details_expose_layout, &x, &y);
728 gdk_draw_layout(widget->window,
729 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
732 _sat_details_expose_layout);
734 buffer = g_strdup_printf("PDOP: %.01f", _gps.pdop);
735 pango_layout_set_text(_sat_details_expose_layout, buffer, -1);
736 pango_layout_get_pixel_size(_sat_details_expose_layout, &x, &y);
737 gdk_draw_layout(widget->window,
738 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
740 (height/6) - y/2 + 20,
741 _sat_details_expose_layout);
743 buffer = g_strdup_printf("VDOP: %.01f", _gps.vdop);
744 pango_layout_set_text(_sat_details_expose_layout, buffer, -1);
745 pango_layout_get_pixel_size(_sat_details_expose_layout, &x, &y);
746 gdk_draw_layout(widget->window,
747 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
749 (height/6) - y/2 + 40,
750 _sat_details_expose_layout);
753 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
760 static GtkWidget *dialog = NULL;
761 static GtkWidget *table = NULL;
762 static GtkWidget *label = NULL;
763 static GtkWidget *notebook = NULL;
764 printf("%s()\n", __PRETTY_FUNCTION__);
768 dialog = gtk_dialog_new_with_buttons(_("GPS Details"),
769 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
770 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
773 gtk_window_set_default_size(GTK_WINDOW(dialog), 600, 300);
775 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
776 notebook = gtk_notebook_new(), TRUE, TRUE, 0);
779 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
780 table = gtk_table_new(4, 6, FALSE),
781 label = gtk_label_new(_("GPS Information")));
783 _sat_details_panel = gtk_drawing_area_new ();
784 gtk_widget_set_size_request (_sat_details_panel, 300, 300);
785 /* sat details info */
786 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
788 label = gtk_label_new(_("Satellites details")));
789 g_signal_connect (G_OBJECT (_sat_details_panel), "expose_event",
790 G_CALLBACK (sat_details_panel_expose), NULL);
792 gtk_table_attach(GTK_TABLE(table),
793 label = gtk_label_new(_("Latitude")),
794 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, 0, 20, 4);
795 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
796 gtk_table_attach(GTK_TABLE(table),
797 _sdi_lat = gtk_label_new(" --- "),
798 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
799 gtk_misc_set_alignment(GTK_MISC(_sdi_lat), 0.f, 0.5f);
801 gtk_table_attach(GTK_TABLE(table),
802 label = gtk_label_new(_("Longitude")),
803 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, 0, 20, 4);
804 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
805 gtk_table_attach(GTK_TABLE(table),
806 _sdi_lon = gtk_label_new(" --- "),
807 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
808 gtk_misc_set_alignment(GTK_MISC(_sdi_lon), 0.f, 0.5f);
810 gtk_table_attach(GTK_TABLE(table),
811 label = gtk_label_new(_("Speed")),
812 0, 1, 2, 3, GTK_EXPAND | GTK_FILL, 0, 20, 4);
813 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
814 gtk_table_attach(GTK_TABLE(table),
815 _sdi_spd = gtk_label_new(" --- "),
816 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4);
817 gtk_misc_set_alignment(GTK_MISC(_sdi_spd), 0.f, 0.5f);
819 gtk_table_attach(GTK_TABLE(table),
820 label = gtk_label_new(_("Altitude")),
821 0, 1, 3, 4, GTK_EXPAND | GTK_FILL, 0, 20, 4);
822 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
823 gtk_table_attach(GTK_TABLE(table),
824 _sdi_alt = gtk_label_new(" --- "),
825 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
826 gtk_misc_set_alignment(GTK_MISC(_sdi_alt), 0.f, 0.5f);
828 gtk_table_attach(GTK_TABLE(table),
829 label = gtk_label_new(_("Heading")),
830 0, 1, 4, 5, GTK_EXPAND | GTK_FILL, 0, 20, 4);
831 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
832 gtk_table_attach(GTK_TABLE(table),
833 _sdi_hea = gtk_label_new(" --- "),
834 1, 2, 4, 5, GTK_EXPAND | GTK_FILL, 0, 2, 4);
835 gtk_misc_set_alignment(GTK_MISC(_sdi_hea), 0.f, 0.5f);
837 gtk_table_attach(GTK_TABLE(table),
838 label = gtk_label_new(_("Local time")),
839 0, 1, 5, 6, GTK_EXPAND | GTK_FILL, 0, 20, 4);
840 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
841 gtk_table_attach(GTK_TABLE(table),
842 _sdi_tim = gtk_label_new(" --:--:-- "),
843 1, 2, 5, 6, GTK_EXPAND | GTK_FILL, 0, 2, 4);
844 gtk_misc_set_alignment(GTK_MISC(_sdi_tim), 0.f, 0.5f);
846 gtk_table_attach(GTK_TABLE(table),
847 label = gtk_label_new(_("Sat in view")),
848 2, 3, 0, 1, GTK_EXPAND | GTK_FILL, 0, 20, 4);
849 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
850 gtk_table_attach(GTK_TABLE(table),
851 _sdi_vie = gtk_label_new("0"),
852 3, 4, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
853 gtk_misc_set_alignment(GTK_MISC(_sdi_vie), 0.f, 0.5f);
855 gtk_table_attach(GTK_TABLE(table),
856 label = gtk_label_new(_("Sat in use")),
857 2, 3, 1, 2, GTK_EXPAND | GTK_FILL, 0, 20, 4);
858 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
859 gtk_table_attach(GTK_TABLE(table),
860 _sdi_use = gtk_label_new("0"),
861 3, 4, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
862 gtk_misc_set_alignment(GTK_MISC(_sdi_use), 0.f, 0.5f);
864 gtk_table_attach(GTK_TABLE(table),
865 label = gtk_label_new(_("Fix")),
866 2, 3, 2, 3, GTK_EXPAND | GTK_FILL, 0, 20, 4);
867 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
868 gtk_table_attach(GTK_TABLE(table),
869 _sdi_fix = gtk_label_new(_("nofix")),
870 3, 4, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4);
871 gtk_misc_set_alignment(GTK_MISC(_sdi_fix), 0.f, 0.5f);
873 gtk_table_attach(GTK_TABLE(table),
874 label = gtk_label_new(_("Fix Quality")),
875 2, 3, 3, 4, GTK_EXPAND | GTK_FILL, 0, 20, 4);
876 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
877 gtk_table_attach(GTK_TABLE(table),
878 _sdi_fqu = gtk_label_new(_("none")),
879 3, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
880 gtk_misc_set_alignment(GTK_MISC(_sdi_fqu), 0.f, 0.5f);
882 gtk_table_attach(GTK_TABLE(table),
883 label = gtk_label_new(_("Max speed")),
884 2, 3, 5, 6, GTK_EXPAND | GTK_FILL, 0, 20, 4);
885 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
886 gtk_table_attach(GTK_TABLE(table),
887 _sdi_msp = gtk_label_new(" --- "),
888 3, 4, 5, 6, GTK_EXPAND | GTK_FILL, 0, 2, 4);
889 gtk_misc_set_alignment(GTK_MISC(_sdi_msp), 0.f, 0.5f);
892 gtk_widget_show_all(dialog);
893 _satdetails_on = TRUE;
894 gps_display_details();
895 while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
897 _satdetails_on = FALSE;
900 gtk_widget_hide(dialog);
902 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
907 * Render a single track line to _map_pixmap. If either point on the line
908 * is a break (defined as unity == 0), a circle is drawn at the other point.
909 * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
912 map_render_segment(GdkGC *gc_norm, GdkGC *gc_alt,
913 gint unitx1, gint unity1, gint unitx2, gint unity2)
915 /* vprintf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
916 unitx1, unity1, unitx2, unity2); */
921 unit2buf(unitx2, unity2, x2, y2);
922 if(((unsigned)(x2+_draw_width) <= _view_width_pixels+2*_draw_width)
923 &&((unsigned)(y2+_draw_width) <= _view_height_pixels+2*_draw_width))
925 gdk_draw_arc(_map_pixmap, gc_alt,
926 FALSE, /* FALSE: not filled. */
931 0, /* start at 0 degrees. */
938 unit2buf(unitx1, unity1, x1, y1);
939 if(((unsigned)(x1+_draw_width) <= _view_width_pixels+2*_draw_width)
940 &&((unsigned)(y1+_draw_width) <= _view_height_pixels+2*_draw_width))
942 gdk_draw_arc(_map_pixmap, gc_alt,
943 FALSE, /* FALSE: not filled. */
948 0, /* start at 0 degrees. */
955 unit2buf(unitx1, unity1, x1, y1);
956 unit2buf(unitx2, unity2, x2, y2);
957 /* Make sure this line could possibly be visible. */
958 if(!((x1 > _view_width_pixels && x2 > _view_width_pixels)
959 || (x1 < 0 && x2 < 0)
960 || (y1 > _view_height_pixels && y2 > _view_height_pixels)
961 || (y1 < 0 && y2 < 0)))
962 gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2);
965 /* vprintf("%s(): return\n", __PRETTY_FUNCTION__); */
969 * Render all track data onto the _map_pixmap. Note that this does not
970 * clear the pixmap of previous track data (use map_force_redraw() for
971 * that), and also note that this method does not queue any redraws, so it
972 * is up to the caller to decide which part of the track really needs to be
976 map_render_path(Path *path, GdkGC **gc)
980 printf("%s()\n", __PRETTY_FUNCTION__);
982 /* gc is a pointer to the first GC to use (for plain points). (gc + 1)
983 * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
984 * to the GC to use for breaks. */
986 /* else there is a route to draw. */
987 for(curr = path->head, wcurr = path->whead; curr++ != path->tail; )
989 /* Draw the line from (curr - 1) to (curr). */
990 map_render_segment(gc[0], gc[2],
991 curr[-1].unitx, curr[-1].unity, curr->unitx, curr->unity);
993 /* Now, check if curr is a waypoint. */
994 if(wcurr <= path->wtail && wcurr->point == curr)
997 unit2buf(wcurr->point->unitx, wcurr->point->unity, x1, y1);
998 gdk_draw_arc(_map_pixmap,
1000 FALSE, /* FALSE: not filled. */
1005 0, /* start at 0 degrees. */
1011 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1017 printf("%s()\n", __PRETTY_FUNCTION__);
1019 if((_show_paths & ROUTES_MASK) && _route.head != _route.tail)
1022 map_render_path(&_route, _gc + COLORABLE_ROUTE);
1024 next_way = path_get_next_way();
1026 /* Now, draw the next waypoint on top of all other waypoints. */
1030 unit2buf(next_way->point->unitx, next_way->point->unity, x1, y1);
1031 /* Draw the next waypoint as a break. */
1032 gdk_draw_arc(_map_pixmap,
1033 _gc[COLORABLE_ROUTE_BREAK],
1034 FALSE, /* FALSE: not filled. */
1039 0, /* start at 0 degrees. */
1043 if(_show_paths & TRACKS_MASK)
1044 map_render_path(&_track, _gc + COLORABLE_TRACK);
1046 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1050 * Update all GdkGC objects to reflect the current _draw_width.
1052 #define UPDATE_GC(gc) \
1053 gdk_gc_set_line_attributes(gc, \
1054 _draw_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1059 printf("%s()\n", __PRETTY_FUNCTION__);
1061 for(i = 0; i < COLORABLE_ENUM_COUNT; i++)
1063 gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color[i]);
1065 g_object_unref(_gc[i]);
1066 _gc[i] = gdk_gc_new(_map_pixmap);
1067 gdk_gc_set_foreground(_gc[i], &_color[i]);
1068 gdk_gc_set_line_attributes(_gc[i],
1069 _draw_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1072 /* Update the _map_widget's gc's. */
1073 gdk_gc_set_line_attributes(
1074 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
1075 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1076 gdk_gc_set_line_attributes(
1077 _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
1078 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1080 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1084 * Call gtk_window_present() on Maemo Mapper. This also checks the
1085 * configuration and brings up the Settings dialog if the GPS Receiver is
1086 * not set up, the first time it is called.
1091 static gint been_here = 0;
1092 static gint done_here = 0;
1093 printf("%s()\n", __PRETTY_FUNCTION__);
1097 /* Set connection state first, to avoid going into this if twice. */
1102 gtk_window_present(GTK_WINDOW(_window));
1104 confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),
1105 _("It looks like this is your first time running"
1106 " Maemo Mapper. Press OK to view the the help pages."
1107 " Otherwise, press Cancel to continue."));
1109 if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
1112 hildon_help_show(_osso, HELP_ID_INTRO, 0);
1114 ossohelp_show(_osso, HELP_ID_INTRO, 0);
1117 gtk_widget_destroy(confirm);
1119 /* Present the settings dialog. */
1121 popup_error(_window,
1122 _("OpenStreetMap.org provides public, free-to-use maps. "
1123 "You can also download a sample set of repositories from "
1124 " the internet by using the \"Download...\" button."));
1126 /* Present the repository dialog. */
1128 confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),
1129 _("You will now see a blank screen. You can download"
1130 " maps using the \"Manage Maps\" menu item in the"
1131 " \"Maps\" menu. Or, press OK now to enable"
1132 " Auto-Download."));
1133 if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
1135 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
1136 _menu_maps_auto_download_item), TRUE);
1138 gtk_widget_destroy(confirm);
1141 /* Connect to receiver. */
1145 ++done_here; /* Don't ask... */
1149 gtk_window_present(GTK_WINDOW(_window));
1150 g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
1151 250, (GSourceFunc)banner_reset, NULL, NULL);
1154 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1159 * "Set" the mark, which translates the current GPS position into on-screen
1160 * units in preparation for drawing the mark with map_draw_mark().
1165 gfloat sqrt_speed, tmp, vel_offset_devx, vel_offset_devy;
1166 printf("%s()\n", __PRETTY_FUNCTION__);
1168 tmp = deg2rad(_gps.heading);
1169 sqrt_speed = VELVEC_SIZE_FACTOR * sqrtf(10 + _gps.speed);
1170 gdk_pixbuf_rotate_vector(&vel_offset_devx, &vel_offset_devy,
1172 sqrt_speed * sinf(tmp), -sqrt_speed * cosf(tmp));
1174 unit2buf(_pos.unitx, _pos.unity, _mark_bufx1, _mark_bufy1);
1176 _mark_bufx2 = _mark_bufx1 + (_show_velvec ? (vel_offset_devx + 0.5f) : 0);
1177 _mark_bufy2 = _mark_bufy1 + (_show_velvec ? (vel_offset_devy + 0.5f) : 0);
1179 _mark_minx = MIN(_mark_bufx1, _mark_bufx2) - (2 * _draw_width);
1180 _mark_miny = MIN(_mark_bufy1, _mark_bufy2) - (2 * _draw_width);
1181 _mark_width = abs(_mark_bufx1 - _mark_bufx2) + (4 * _draw_width);
1182 _mark_height = abs(_mark_bufy1 - _mark_bufy2) + (4 * _draw_width);
1184 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1189 * Force a redraw of the entire _map_pixmap, including fetching the
1190 * background maps from disk and redrawing the tracks on top of them.
1195 printf("%s()\n", __PRETTY_FUNCTION__);
1199 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
1203 _view_width_pixels, _view_height_pixels,
1204 GDK_RGB_DITHER_NONE, 0, 0);
1206 MACRO_MAP_RENDER_DATA();
1207 MACRO_QUEUE_DRAW_AREA();
1209 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1213 map_calc_new_center(gint zoom)
1216 printf("%s()\n", __PRETTY_FUNCTION__);
1218 switch(_center_mode)
1222 gfloat tmp = deg2rad(_gps.heading);
1223 gfloat screen_pixels = _view_width_pixels
1224 + (((gint)_view_height_pixels
1225 - (gint)_view_width_pixels)
1226 * fabsf(cosf(deg2rad(
1227 ROTATE_DIR_ENUM_DEGREES[_rotate_dir] -
1229 : (_next_map_rotate_angle
1230 - (gint)(_gps.heading)))))));
1231 gfloat lead_pixels = 0.0025f
1232 * pixel2zunit((gint)screen_pixels, zoom)
1234 * VELVEC_SIZE_FACTOR
1235 * (_lead_is_fixed ? 7 : sqrtf(_gps.speed));
1237 new_center.unitx = _pos.unitx + (gint)(lead_pixels * sinf(tmp));
1238 new_center.unity = _pos.unity - (gint)(lead_pixels * cosf(tmp));
1242 new_center.unitx = _pos.unitx;
1243 new_center.unity = _pos.unity;
1246 new_center.unitx = _next_center.unitx;
1247 new_center.unity = _next_center.unity;
1250 vprintf("%s(): return (%d, %d)\n", __PRETTY_FUNCTION__,
1251 new_center.unitx, new_center.unity);
1256 * Center the view on the given unitx/unity.
1259 map_center_unit_full(Point new_center,
1260 gint zoom, gint rotate_angle)
1263 printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
1264 new_center.unitx, new_center.unity);
1268 /* Assure that _center.unitx/y are bounded. */
1269 BOUND(new_center.unitx, 0, WORLD_SIZE_UNITS);
1270 BOUND(new_center.unity, 0, WORLD_SIZE_UNITS);
1272 mrt = g_slice_new(MapRenderTask);
1273 mrt->repo = _curr_repo;
1274 mrt->old_offsetx = _map_offset_devx;
1275 mrt->old_offsety = _map_offset_devy;
1276 mrt->new_center = _next_center = new_center;
1277 mrt->screen_width_pixels = _view_width_pixels;
1278 mrt->screen_height_pixels = _view_height_pixels;
1279 mrt->zoom = _next_zoom = zoom;
1280 mrt->rotate_angle = _next_map_rotate_angle = rotate_angle;
1283 gtk_widget_queue_draw_area(
1285 _redraw_wait_bounds.x,
1286 _redraw_wait_bounds.y,
1287 _redraw_wait_bounds.width
1288 + (_redraw_count * HOURGLASS_SEPARATION),
1289 _redraw_wait_bounds.height);
1293 g_thread_pool_push(_mrt_thread_pool, mrt, NULL);
1296 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1300 map_center_unit(Point new_center)
1302 map_center_unit_full(new_center, _next_zoom,
1303 _center_mode > 0 && _center_rotate
1304 ? _gps.heading : _next_map_rotate_angle);
1308 map_rotate(gint rotate_angle)
1310 if(_center_mode > 0 && gtk_check_menu_item_get_active(
1311 GTK_CHECK_MENU_ITEM(_menu_view_rotate_auto_item)))
1312 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
1313 _menu_view_rotate_auto_item), FALSE);
1315 map_center_unit_full(map_calc_new_center(_next_zoom), _next_zoom,
1316 (_next_map_rotate_angle + rotate_angle) % 360);
1320 map_center_zoom(gint zoom)
1322 map_center_unit_full(map_calc_new_center(zoom), zoom,
1323 _center_mode > 0 && _center_rotate
1324 ? _gps.heading : _next_map_rotate_angle);
1328 * Pan the view by the given number of units in the X and Y directions.
1331 map_pan(gint delta_unitx, gint delta_unity)
1334 printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, delta_unitx, delta_unity);
1336 if(_center_mode > 0)
1337 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
1338 _menu_view_ac_none_item), TRUE);
1339 new_center.unitx = _center.unitx + delta_unitx;
1340 new_center.unity = _center.unity + delta_unity;
1341 map_center_unit_full(new_center, _next_zoom,
1342 _center_mode > 0 && _center_rotate
1343 ? _gps.heading : _next_map_rotate_angle);
1345 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1349 * Initiate a move of the mark from the old location to the current
1350 * location. This function queues the draw area of the old mark (to force
1351 * drawing of the background map), then updates the mark, then queus the
1352 * draw area of the new mark.
1357 printf("%s()\n", __PRETTY_FUNCTION__);
1359 /* Just queue the old and new draw areas. */
1360 gtk_widget_queue_draw_area(_map_widget,
1361 _mark_minx + _map_offset_devx,
1362 _mark_miny + _map_offset_devy,
1366 gtk_widget_queue_draw_area(_map_widget,
1367 _mark_minx + _map_offset_devx,
1368 _mark_miny + _map_offset_devy,
1372 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1376 * Make sure the mark is up-to-date. This function triggers a panning of
1377 * the view if the mark is appropriately close to the edge of the view.
1380 map_refresh_mark(gboolean force_redraw)
1382 printf("%s()\n", __PRETTY_FUNCTION__);
1384 gint new_center_devx, new_center_devy;
1386 Point new_center = map_calc_new_center(_next_zoom);
1388 unit2buf(new_center.unitx, new_center.unity,
1389 new_center_devx, new_center_devy);
1390 if(force_redraw || (_center_mode > 0
1391 && (UNITS_CONVERT[_units] * _gps.speed) >= _ac_min_speed &&
1392 (((unsigned)(new_center_devx - (_view_width_pixels * _center_ratio / 20))
1393 > ((10 - _center_ratio) * _view_width_pixels / 10))
1394 || ((unsigned)(new_center_devy - (_view_height_pixels * _center_ratio / 20))
1395 > ((10 - _center_ratio) * _view_height_pixels / 10))
1396 || (_center_rotate &&
1397 abs(_next_map_rotate_angle - _gps.heading)
1398 > (4*(10-_rotate_sens))))))
1401 map_center_unit(new_center);
1405 /* We're not changing the view - just move the mark. */
1409 /* Draw speed info */
1413 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1417 map_download_refresh_idle(MapUpdateTask *mut)
1419 vprintf("%s(%p, %d, %d, %d)\n", __PRETTY_FUNCTION__, mut,
1420 mut->zoom, mut->tilex, mut->tiley);
1422 /* Test if download succeeded (only if retries != 0). */
1425 gint zoff = mut->zoom - _zoom;
1426 /* Update the UI to reflect the updated map database. */
1427 /* Only refresh at same or "lower" (more detailed) zoom level. */
1428 if(mut->update_type == MAP_UPDATE_AUTO && (unsigned)zoff <= 4)
1430 gfloat destx, desty;
1431 gint boundx, boundy, width, height;
1434 tile2pixel(mut->tilex << zoff)
1435 + ((TILE_SIZE_PIXELS << zoff) >> 1),
1436 tile2pixel(mut->tiley << zoff)
1437 + ((TILE_SIZE_PIXELS << zoff) >> 1),
1440 /* Multiply the matrix to cause blitting. */
1442 gdk_pixbuf_rotate_matrix_mult_number(
1443 _map_rotate_matrix, 1 << zoff);
1444 gdk_pixbuf_rotate(_map_pixbuf,
1445 /* Apply Map Correction. */
1446 destx - unit2pixel(_map_correction_unitx),
1447 desty - unit2pixel(_map_correction_unity),
1450 TILE_SIZE_PIXELS / 2,
1451 TILE_SIZE_PIXELS / 2,
1454 &boundx, &boundy, &width, &height);
1455 /* Un-multiply the matrix that we used for blitting. Good thing
1456 * we're multiplying by powers of two, or this wouldn't work
1457 * consistently... */
1459 gdk_pixbuf_rotate_matrix_mult_number(
1460 _map_rotate_matrix, 1.f / (1 << zoff));
1466 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
1471 GDK_RGB_DITHER_NONE, 0, 0);
1472 MACRO_MAP_RENDER_DATA();
1473 gtk_widget_queue_draw_area(
1474 _map_widget, boundx, boundy, width, height);
1477 g_object_unref(mut->pixbuf);
1479 else if(mut->vfs_result != GNOME_VFS_OK)
1484 if(++_curr_download == _num_downloads)
1486 if(_download_banner)
1488 gtk_widget_destroy(_download_banner);
1489 _download_banner = NULL;
1491 _num_downloads = _curr_download = 0;
1492 g_thread_pool_stop_unused_threads();
1493 #ifndef MAPDB_SQLITE
1495 gdbm_sync(_curr_repo->db);
1499 if (mut->repo->layer_level == 0) {
1500 gchar buffer[BUFFER_SIZE];
1501 snprintf(buffer, sizeof(buffer), "%d %s", _dl_errors,
1502 _("maps failed to download."));
1503 MACRO_BANNER_SHOW_INFO(_window, buffer);
1508 if(mut->update_type != MAP_UPDATE_AUTO || _refresh_map_after_download)
1510 /* Update the map. */
1511 map_refresh_mark(TRUE);
1514 else if(_download_banner)
1516 hildon_banner_set_fraction(HILDON_BANNER(_download_banner),
1517 _curr_download / (double)_num_downloads);
1520 g_mutex_lock(_mut_priority_mutex);
1521 g_hash_table_remove(_mut_exists_table, mut);
1522 g_mutex_unlock(_mut_priority_mutex);
1524 g_slice_free(MapUpdateTask, mut);
1526 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1531 * Set the current zoom level. If the given zoom level is the same as the
1532 * current zoom level, or if the new zoom is invalid
1533 * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
1536 map_set_zoom(gint new_zoom)
1538 printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom);
1540 /* This if condition also checks for new_zoom >= 0. */
1541 if((unsigned)new_zoom > MAX_ZOOM)
1544 map_center_zoom(new_zoom / _curr_repo->view_zoom_steps
1545 * _curr_repo->view_zoom_steps);
1547 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1551 map_replace_pixbuf_idle(MapRenderTask *mrt)
1553 printf("%s()\n", __PRETTY_FUNCTION__);
1555 if(!--_pending_replaces && !_mouse_is_down
1556 && mrt->screen_width_pixels == _view_width_pixels
1557 && mrt->screen_height_pixels == _view_height_pixels)
1559 g_object_unref(_map_pixbuf);
1560 _map_pixbuf = mrt->pixbuf;
1562 if(_center.unitx != mrt->new_center.unitx
1563 || _center.unity != mrt->new_center.unity
1564 || _zoom != mrt->zoom
1565 || _map_rotate_angle != mrt->rotate_angle)
1567 dbus_ifc_fire_view_position_changed(
1568 mrt->new_center, mrt->zoom, mrt->rotate_angle);
1571 _center = mrt->new_center;
1573 _map_rotate_angle = mrt->rotate_angle;
1575 gdk_pixbuf_rotate_matrix_fill_for_rotation(
1577 deg2rad(ROTATE_DIR_ENUM_DEGREES[_rotate_dir]
1578 - _map_rotate_angle));
1579 gdk_pixbuf_rotate_matrix_fill_for_rotation(
1580 _map_reverse_matrix,
1581 deg2rad(_map_rotate_angle
1582 - ROTATE_DIR_ENUM_DEGREES[_rotate_dir]));
1586 _map_offset_devx = 0;
1587 _map_offset_devy = 0;
1594 /* Ignore this new pixbuf. We have newer ones coming. */
1595 g_object_unref(mrt->pixbuf);
1599 g_slice_free(MapRenderTask, mrt);
1601 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1606 /* Routine draws one partly-transparent pixbuf on top of the another (base map). For efficiency, we
1607 assume that base map's tile have no transparent pixels (because it should not have them). We also
1608 assume that pixbufs are have the same size. */
1610 combine_tiles (GdkPixbuf *dst_pixbuf, GdkPixbuf *src_pixbuf)
1612 gint s_n_channels = gdk_pixbuf_get_n_channels (src_pixbuf);
1613 gint d_n_channels = gdk_pixbuf_get_n_channels (dst_pixbuf);
1614 gint bps = gdk_pixbuf_get_bits_per_sample (dst_pixbuf);
1615 gint width, height, x, y, d_delta, s_delta;
1618 if (gdk_pixbuf_get_colorspace (dst_pixbuf) != gdk_pixbuf_get_colorspace (src_pixbuf)) {
1619 printf ("combine return (1)\n");
1622 if (gdk_pixbuf_get_colorspace (dst_pixbuf) != GDK_COLORSPACE_RGB) {
1623 printf ("combine return (2)\n");
1627 if (bps != gdk_pixbuf_get_bits_per_sample (src_pixbuf)) {
1628 printf ("combine return (5)\n");
1632 width = gdk_pixbuf_get_width (dst_pixbuf);
1633 height = gdk_pixbuf_get_height (dst_pixbuf);
1635 if (width != gdk_pixbuf_get_width (src_pixbuf)) {
1636 printf ("combine return (6)\n");
1639 if (height != gdk_pixbuf_get_height (src_pixbuf)) {
1640 printf ("combine return (7)\n");
1644 s_delta = (bps >> 3) * s_n_channels;
1645 d_delta = (bps >> 3) * d_n_channels;
1646 d_p = gdk_pixbuf_get_pixels (dst_pixbuf);
1647 s_p = gdk_pixbuf_get_pixels (src_pixbuf);
1649 /* ok, we're ready to combine */
1650 for (y = 0; y < height; y++) {
1651 for (x = 0; x < width; x++, d_p += d_delta, s_p += s_delta) {
1652 /* TODO: alpha blending? */
1653 if (s_n_channels == 3 || s_p[3]) {
1664 thread_render_map(MapRenderTask *mrt)
1667 gint start_tilex, start_tiley, stop_tilex, stop_tiley;
1668 gint x = 0, y, num_tilex, num_tiley;
1669 gint diag_halflength_units;
1671 gint tile_rothalf_pixels;
1672 gint curr_tile_to_draw, num_tiles_to_draw;
1674 ThreadLatch *refresh_latch = NULL;
1676 static gint8 auto_download_batch_id = INT8_MIN;
1677 printf("%s(%d, %d, %d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
1678 mrt->screen_width_pixels, mrt->screen_height_pixels,
1679 mrt->new_center.unitx, mrt->new_center.unity, mrt->zoom,
1682 /* If there are more render tasks in the queue, skip this one. */
1683 if(g_thread_pool_unprocessed(_mrt_thread_pool))
1685 g_slice_free(MapRenderTask, mrt);
1687 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1691 angle_rad = deg2rad(ROTATE_DIR_ENUM_DEGREES[_rotate_dir]
1692 - mrt->rotate_angle);
1694 gdk_pixbuf_rotate_matrix_fill_for_rotation(matrix, angle_rad);
1696 /* Determine (roughly) the tiles we might have to process.
1697 * Basically, we take the center unit and subtract the maximum dimension
1698 * of the screen plus the maximum additional pixels of a rotated tile.
1700 tile_rothalf_pixels = MAX(
1701 fabsf(TILE_HALFDIAG_PIXELS * sinf((PI / 4) - angle_rad)),
1702 fabsf(TILE_HALFDIAG_PIXELS * cosf((PI / 4) - angle_rad)));
1704 mrt->zoom = _next_zoom;
1706 if(mrt->repo->type != REPOTYPE_NONE && mrt->repo->db)
1707 cache_amount = _auto_download_precache;
1709 cache_amount = 1; /* No cache. */
1711 diag_halflength_units = pixel2zunit(TILE_HALFDIAG_PIXELS
1712 + MAX(mrt->screen_width_pixels, mrt->screen_height_pixels) / 2,
1714 start_tilex = unit2ztile(
1715 mrt->new_center.unitx - diag_halflength_units
1716 + _map_correction_unitx, mrt->zoom);
1717 start_tilex = MAX(start_tilex - (cache_amount - 1), 0);
1718 start_tiley = unit2ztile(
1719 mrt->new_center.unity - diag_halflength_units
1720 + _map_correction_unity, mrt->zoom);
1721 start_tiley = MAX(start_tiley - (cache_amount - 1), 0);
1722 stop_tilex = unit2ztile(mrt->new_center.unitx + diag_halflength_units
1723 + _map_correction_unitx, mrt->zoom);
1724 stop_tilex = MIN(stop_tilex + (cache_amount - 1),
1725 unit2ztile(WORLD_SIZE_UNITS, mrt->zoom));
1726 stop_tiley = unit2ztile(mrt->new_center.unity + diag_halflength_units
1727 + _map_correction_unity, mrt->zoom);
1728 stop_tiley = MIN(stop_tiley + (cache_amount - 1),
1729 unit2ztile(WORLD_SIZE_UNITS, mrt->zoom));
1731 num_tilex = (stop_tilex - start_tilex + 1);
1732 num_tiley = (stop_tiley - start_tiley + 1);
1733 tile_dev = g_new0(gfloat, num_tilex * num_tiley * 2);
1735 ++auto_download_batch_id;
1737 /* Iterate through the tiles and mark which ones need retrieval. */
1738 num_tiles_to_draw = 0;
1739 for(y = 0; y < num_tiley; ++y)
1741 for(x = 0; x < num_tilex; ++x)
1745 /* Find the device location of this tile's center. */
1747 tile2pixel(x + start_tilex) + (TILE_SIZE_PIXELS >> 1),
1748 tile2pixel(y + start_tiley) + (TILE_SIZE_PIXELS >> 1),
1750 mrt->new_center, mrt->zoom, matrix);
1752 /* Apply Map Correction. */
1753 devx -= unit2zpixel(_map_correction_unitx, mrt->zoom);
1754 devy -= unit2zpixel(_map_correction_unity, mrt->zoom);
1756 /* Skip this tile under the following conditions:
1757 * devx < -tile_rothalf_pixels
1758 * devx > _view_width_pixels + tile_rothalf_pixels
1759 * devy < -tile_rothalf_pixels
1760 * devy > _view_height_pixels + tile_rothalf_pixels
1762 if(((unsigned)(devx + tile_rothalf_pixels))
1763 < (_view_width_pixels + (2 * tile_rothalf_pixels))
1764 && ((unsigned)(devy + tile_rothalf_pixels))
1765 < (_view_height_pixels + (2 * tile_rothalf_pixels)))
1767 tile_dev[2 * (y * num_tilex + x)] = devx;
1768 tile_dev[2 * (y * num_tilex + x) + 1] = devy;
1769 ++num_tiles_to_draw;
1773 tile_dev[2 * (y * num_tilex + x)] = FLT_MAX;
1778 mrt->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
1779 mrt->screen_width_pixels, mrt->screen_height_pixels);
1780 _refresh_map_after_download = FALSE;
1782 /* Iterate through the tiles, get them (or queue a download if they're
1783 * not in the cache), and rotate them into the pixbuf. */
1784 for(y = curr_tile_to_draw = 0; y < num_tiley; ++y)
1786 gint tiley = y + start_tiley;
1787 for(x = 0; x < num_tilex; ++x)
1789 GdkPixbuf *tile_pixbuf = NULL, *layer_pixbuf = NULL;
1790 gboolean started_download = FALSE;
1791 gint zoff, zoff_base;
1793 RepoData* repo_p = mrt->repo;
1795 tilex = x + start_tilex;
1796 zoff_base = mrt->repo->double_size ? 1 : 0;
1798 /* iterating over tile and all it's layers */
1801 started_download = FALSE;
1803 /* for layers we must use resolution of underlying map */
1806 /* if this is not a bottom layer and layer not enabled, skip it */
1807 if (repo_p != mrt->repo && !repo_p->layer_enabled)
1809 repo_p = repo_p->layers;
1813 /* Iteratively try to retrieve a map to draw the tile. */
1814 while((mrt->zoom + zoff) <= MAX_ZOOM && zoff < 4)
1816 /* Check if we're actually going to draw this map. */
1817 if(tile_dev[2 * (y*num_tilex + x)] != FLT_MAX)
1819 if(NULL != (layer_pixbuf = mapdb_get(
1820 repo_p, mrt->zoom + zoff,
1824 /* Found a map. Check for it's age. */
1825 gint age = get_tile_age (layer_pixbuf);
1826 printf ("Tile age (%d)\n", age);
1828 /* throw away tile only if we can download something */
1829 if (!repo_p->layer_refresh_interval ||
1830 age < repo_p->layer_refresh_interval * 60 ||
1833 /* if this is a layer's tile, join with main tile */
1834 if (repo_p != mrt->repo)
1836 /* but only if main layer is exists */
1838 combine_tiles (tile_pixbuf, layer_pixbuf);
1839 g_object_unref (layer_pixbuf);
1842 tile_pixbuf = layer_pixbuf;
1848 g_object_unref (layer_pixbuf);
1852 _refresh_map_after_download = TRUE;
1854 /* Else we're not going to be drawing this map, so just check
1855 * if it's in the database. */
1856 else if(mapdb_exists(
1857 repo_p, mrt->zoom + zoff,
1865 /* No map; download, if we should. */
1866 if(!started_download && _auto_download
1867 && mrt->repo->type != REPOTYPE_NONE
1868 /* Make sure this map is within dl zoom limits. */
1869 && ((unsigned)(mrt->zoom + zoff - mrt->repo->min_zoom)
1870 <= (mrt->repo->max_zoom - mrt->repo->min_zoom))
1871 /* Make sure this map matches the dl_zoom_steps,
1872 * or that there currently is no cache. */
1873 && (!repo_p->db || !((mrt->zoom + zoff
1874 - (mrt->repo->double_size ? 1 : 0))
1875 % mrt->repo->dl_zoom_steps))
1876 /* Make sure this tile is even possible. */
1877 && ((unsigned)(tilex >> zoff)
1878 < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)
1879 && (unsigned)(tiley >> zoff)
1880 < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)))
1882 started_download = TRUE;
1886 refresh_latch = g_slice_new(ThreadLatch);
1887 refresh_latch->is_open = FALSE;
1888 refresh_latch->is_done_adding_tasks = FALSE;
1889 refresh_latch->num_tasks = 1;
1890 refresh_latch->num_done = 0;
1891 refresh_latch->mutex = g_mutex_new();
1892 refresh_latch->cond = g_cond_new();
1895 ++refresh_latch->num_tasks;
1897 mapdb_initiate_update(
1903 auto_download_batch_id,
1904 (abs((tilex >> zoff) - unit2ztile(
1905 mrt->new_center.unitx, mrt->zoom + zoff))
1906 + abs((tiley >> zoff) - unit2ztile(
1907 mrt->new_center.unity, mrt->zoom + zoff))),
1911 /* Try again at a coarser resolution. Only for underlying map.*/
1912 if (repo_p == mrt->repo && repo_p->type != REPOTYPE_NONE)
1918 repo_p = repo_p->layers;
1921 /* use zoom of the base map */
1926 gint boundx, boundy, width, height;
1929 gdk_pixbuf_rotate_matrix_mult_number(matrix, 1 << zoff);
1930 gdk_pixbuf_rotate(mrt->pixbuf,
1931 tile_dev[2 * (y * num_tilex + x)],
1932 tile_dev[2 * (y * num_tilex + x) + 1],
1935 ((tilex - ((tilex >> zoff) << zoff))
1936 << (TILE_SIZE_P2 - zoff))
1937 + (TILE_SIZE_PIXELS >> (1 + zoff)),
1938 ((tiley - ((tiley>>zoff) << zoff))
1939 << (TILE_SIZE_P2 - zoff))
1940 + (TILE_SIZE_PIXELS >> (1 + zoff)),
1941 TILE_SIZE_PIXELS >> zoff,
1942 TILE_SIZE_PIXELS >> zoff,
1943 &boundx, &boundy, &width, &height);
1944 g_object_unref(tile_pixbuf);
1945 /* Un-multiply the matrix that we used for blitting. Good
1946 * thing we're multiplying by powers of two, or this wouldn't
1947 * work consistently... */
1949 gdk_pixbuf_rotate_matrix_mult_number(
1950 matrix, 1.f / (1 << zoff));
1952 /* usleep(10000); DEBUG */
1956 /* Don't replace the pixbuf unless/until the mouse is released. */
1957 g_mutex_lock(_mouse_mutex);
1958 ++_pending_replaces;
1959 g_idle_add_full(G_PRIORITY_HIGH_IDLE + 20,
1960 (GSourceFunc)map_replace_pixbuf_idle, mrt, NULL);
1961 g_mutex_unlock(_mouse_mutex);
1963 /* Release the view-change lock. */
1966 g_mutex_lock(refresh_latch->mutex);
1967 if(refresh_latch->num_tasks == refresh_latch->num_done)
1969 /* Fast little workers, aren't they? */
1970 g_mutex_unlock(refresh_latch->mutex);
1971 g_cond_free(refresh_latch->cond);
1972 g_mutex_free(refresh_latch->mutex);
1973 g_slice_free(ThreadLatch, refresh_latch);
1977 refresh_latch->is_done_adding_tasks = TRUE;
1978 refresh_latch->is_open = TRUE;
1979 g_cond_signal(refresh_latch->cond);
1980 g_mutex_unlock(refresh_latch->mutex);
1986 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1991 map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
1993 gint old_view_width_pixels, old_view_height_pixels;
1994 GdkPixbuf *old_map_pixbuf;
1995 printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
1996 _map_widget->allocation.width, _map_widget->allocation.height);
1998 if(_map_widget->allocation.width == 1
1999 && _map_widget->allocation.height == 1)
2000 /* Special case - first allocation - not persistent. */
2003 old_view_width_pixels = _view_width_pixels;
2004 old_view_height_pixels = _view_height_pixels;
2005 _view_width_pixels = _map_widget->allocation.width;
2006 _view_height_pixels = _map_widget->allocation.height;
2007 _view_halfwidth_pixels = _view_width_pixels / 2;
2008 _view_halfheight_pixels = _view_height_pixels / 2;
2010 g_object_unref(_map_pixmap);
2011 _map_pixmap = gdk_pixmap_new(
2012 _map_widget->window,
2013 _view_width_pixels, _view_height_pixels,
2014 -1); /* -1: use bit depth of widget->window. */
2016 old_map_pixbuf = _map_pixbuf;
2017 _map_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
2018 _view_width_pixels, _view_height_pixels);
2021 gint oldnew_diffx = (gint)(_view_width_pixels
2022 - old_view_width_pixels) / 2;
2023 gint oldnew_diffy = (gint)(_view_height_pixels
2024 - old_view_height_pixels) / 2;
2025 gdk_pixbuf_copy_area(old_map_pixbuf,
2026 MAX(0, -oldnew_diffx), MAX(0, -oldnew_diffy),
2027 MIN(_view_width_pixels, old_view_width_pixels),
2028 MIN(_view_height_pixels, old_view_height_pixels),
2030 MAX(0, oldnew_diffx), MAX(0, oldnew_diffy));
2033 g_object_unref(old_map_pixbuf);
2035 /* Set _scale_rect. */
2036 _scale_rect.x = (_view_width_pixels - SCALE_WIDTH) / 2;
2037 _scale_rect.width = SCALE_WIDTH;
2038 pango_layout_set_text(_scale_layout, "0", -1);
2039 pango_layout_get_pixel_size(_scale_layout, NULL, &_scale_rect.height);
2040 _scale_rect.y = _view_height_pixels - _scale_rect.height - 1;
2042 /* Set _zoom rect. */
2043 pango_layout_set_text(_zoom_layout, "00", -1);
2044 pango_layout_get_pixel_size(_zoom_layout, &_zoom_rect.width,
2045 &_zoom_rect.height);
2046 _zoom_rect.width *= 1.25;
2047 pango_layout_set_width(_zoom_layout, _zoom_rect.width);
2048 pango_layout_context_changed(_zoom_layout);
2049 _zoom_rect.x = _scale_rect.x - _zoom_rect.width;
2050 _zoom_rect.y = _view_height_pixels - _zoom_rect.height - 1;
2052 /* Set _comprose_rect. */
2053 _comprose_rect.x = _view_width_pixels - 25 - _comprose_rect.width;
2054 _comprose_rect.y = _view_height_pixels - 25 - _comprose_rect.height;
2059 /* Fire the screen_dimensions_changed DBUS signal. */
2060 dbus_ifc_fire_view_dimensions_changed(
2061 _view_width_pixels, _view_height_pixels);
2063 /* If Auto-Center is set to Lead, then recalc center. */
2064 if(_center_mode == CENTER_LEAD)
2065 map_center_unit(map_calc_new_center(_next_zoom));
2067 map_center_unit(_next_center);
2069 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2074 sat_panel_expose(GtkWidget *widget, GdkEventExpose *event)
2078 printf("%s()\n", __PRETTY_FUNCTION__);
2080 draw_sat_info(widget,
2082 widget->allocation.width,
2083 widget->allocation.height,
2086 /* Sat View/In Use */
2087 tmp = g_strdup_printf("%d/%d", _gps.satinuse, _gps.satinview);
2088 pango_layout_set_text(_sat_panel_layout, tmp, -1);
2089 pango_layout_set_alignment(_sat_panel_layout, PANGO_ALIGN_LEFT);
2090 gdk_draw_layout(widget->window,
2091 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2099 case 3: tmp = g_strdup_printf("%dD fix", _gps.fix); break;
2100 default: tmp = g_strdup_printf("nofix"); break;
2102 pango_layout_set_text(_sat_panel_layout, tmp, -1);
2103 pango_layout_set_alignment(_sat_panel_layout, PANGO_ALIGN_RIGHT);
2104 pango_layout_get_pixel_size(_sat_panel_layout, &x, &y);
2105 gdk_draw_layout(widget->window,
2106 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2107 widget->allocation.width - 20 - x, 2,
2111 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2116 heading_panel_expose(GtkWidget *widget, GdkEventExpose *event)
2118 gint size, xoffset, yoffset, i, x, y;
2122 printf("%s()\n", __PRETTY_FUNCTION__);
2124 size = MIN(widget->allocation.width, widget->allocation.height);
2125 if(widget->allocation.width > widget->allocation.height)
2127 xoffset = (widget->allocation.width - widget->allocation.height) / 2;
2133 yoffset = (widget->allocation.height - widget->allocation.width) / 2;
2135 pango_font_description_set_size(_heading_panel_fontdesc,12*PANGO_SCALE);
2136 pango_layout_set_font_description(_heading_panel_layout,
2137 _heading_panel_fontdesc);
2138 pango_layout_set_alignment(_heading_panel_layout, PANGO_ALIGN_CENTER);
2140 text = g_strdup_printf("%3.0f°", _gps.heading);
2141 pango_layout_set_text(_heading_panel_layout, text, -1);
2142 pango_layout_get_pixel_size(_heading_panel_layout, &x, &y);
2144 gdk_draw_layout(widget->window,
2145 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2146 xoffset + size/2 - x/2,
2147 yoffset + size - y - 2, _heading_panel_layout);
2150 gdk_draw_arc (widget->window,
2151 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2153 xoffset, yoffset + size/2,
2157 /* Simple arrow for heading*/
2158 gdk_draw_line(widget->window,
2159 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2160 xoffset + size/2 + 3,
2161 yoffset + size - y - 5,
2163 yoffset + size/2 + 5);
2165 gdk_draw_line(widget->window,
2166 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2167 xoffset + size/2 - 3,
2168 yoffset + size - y - 5,
2170 yoffset + size/2 + 5);
2172 gdk_draw_line(widget->window,
2173 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2174 xoffset + size/2 - 3,
2175 yoffset + size - y - 5,
2177 yoffset + size - y - 8);
2179 gdk_draw_line(widget->window,
2180 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2181 xoffset + size/2 + 3,
2182 yoffset + size - y - 5,
2184 yoffset + size - y - 8);
2186 gint angle[5] = {-90,-45,0,45,90};
2187 gint fsize[5] = {0,4,10,4,0};
2188 for(i = 0; i < 5; i++)
2190 dir = (gint)(_gps.heading/45)*45 + angle[i];
2195 case 360: text = g_strdup("N"); break;
2198 text = g_strdup("NE"); break;
2200 text = g_strdup("E"); break;
2202 text = g_strdup("SE"); break;
2204 text = g_strdup("S"); break;
2206 text = g_strdup("SW"); break;
2209 text = g_strdup("W"); break;
2212 text = g_strdup("NW"); break;
2214 text = g_strdup("??");
2218 tmp = deg2rad(dir - _gps.heading);
2219 gdk_draw_line(widget->window,
2220 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2221 xoffset + size/2 + ((size/2 - 5) * sinf(tmp)),
2222 yoffset + size - ((size/2 - 5) * cosf(tmp)),
2223 xoffset + size/2 + ((size/2 + 5) * sinf(tmp)),
2224 yoffset + size - ((size/2 + 5) * cosf(tmp)));
2227 if(abs((gint)(_gps.heading/45)*45 - _gps.heading)
2228 > abs((gint)(_gps.heading/45)*45 + 45 - _gps.heading)
2232 pango_font_description_set_size(_heading_panel_fontdesc,
2233 (10 + x)*PANGO_SCALE);
2234 pango_layout_set_font_description(_heading_panel_layout,
2235 _heading_panel_fontdesc);
2236 pango_layout_set_text(_heading_panel_layout, text, -1);
2237 pango_layout_get_pixel_size(_heading_panel_layout, &x, &y);
2238 x = xoffset + size/2 + ((size/2 + 15) * sinf(tmp)) - x/2,
2239 y = yoffset + size - ((size/2 + 15) * cosf(tmp)) - y/2,
2241 gdk_draw_layout(widget->window,
2242 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2243 x, y, _heading_panel_layout);
2247 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2252 map_cb_expose(GtkWidget *widget, GdkEventExpose *event)
2255 printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
2256 event->area.x, event->area.y,
2257 event->area.width, event->area.height);
2260 _map_widget->window,
2261 _gc[COLORABLE_MARK],
2263 event->area.x - _map_offset_devx, event->area.y - _map_offset_devy,
2264 event->area.x, event->area.y,
2265 event->area.width, event->area.height);
2267 /* Draw the mark. */
2268 if((((unsigned)(_mark_bufx1 + _draw_width)
2269 <= _view_width_pixels+2*_draw_width)
2270 &&((unsigned)(_mark_bufy1 + _draw_width)
2271 <= _view_height_pixels+2*_draw_width))
2272 || (((unsigned)(_mark_bufx2 + _draw_width)
2273 <= _view_width_pixels+2*_draw_width)
2274 &&((unsigned)(_mark_bufy2 + _draw_width)
2275 <= _view_height_pixels+2*_draw_width)))
2278 _map_widget->window,
2279 _gps_state == RCVR_FIXED
2280 ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD],
2281 FALSE, /* not filled. */
2282 _mark_bufx1 - _draw_width + _map_offset_devx,
2283 _mark_bufy1 - _draw_width + _map_offset_devy,
2284 2 * _draw_width, 2 * _draw_width,
2287 _map_widget->window,
2288 _gps_state == RCVR_FIXED
2290 ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK])
2291 : _gc[COLORABLE_MARK_OLD],
2292 _mark_bufx1 + _map_offset_devx,
2293 _mark_bufy1 + _map_offset_devy,
2294 _mark_bufx2 + _map_offset_devx,
2295 _mark_bufy2 + _map_offset_devy);
2298 /* draw zoom box if so wanted */
2299 if(_show_zoomlevel) {
2300 gchar *buffer = g_strdup_printf("%d", _zoom);
2301 gdk_draw_rectangle(_map_widget->window,
2302 _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2304 _zoom_rect.x, _zoom_rect.y,
2305 _zoom_rect.width, _zoom_rect.height);
2306 gdk_draw_rectangle(_map_widget->window,
2307 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2309 _zoom_rect.x, _zoom_rect.y,
2310 _zoom_rect.width, _zoom_rect.height);
2311 pango_layout_set_text(_zoom_layout, buffer, -1);
2312 gdk_draw_layout(_map_widget->window,
2313 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2314 _zoom_rect.x + _zoom_rect.width / 2,
2315 _zoom_rect.y, _zoom_layout);
2318 /* Draw scale, if necessary. */
2321 gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
2322 if(event->area.width && event->area.height)
2324 gdk_draw_rectangle(_map_widget->window,
2325 _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2327 _scale_rect.x, _scale_rect.y,
2328 _scale_rect.width, _scale_rect.height);
2329 gdk_draw_rectangle(_map_widget->window,
2330 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2332 _scale_rect.x, _scale_rect.y,
2333 _scale_rect.width, _scale_rect.height);
2335 /* Now calculate and draw the distance. */
2339 gdouble lat1, lon1, lat2, lon2;
2342 unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4),
2343 _center.unity, lat1, lon1);
2344 unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4),
2345 _center.unity, lat2, lon2);
2346 distance = calculate_distance(lat1, lon1, lat2, lon2)
2347 * UNITS_CONVERT[_units];
2350 snprintf(buffer, sizeof(buffer), "%0.2f %s", distance,
2351 UNITS_ENUM_TEXT[_units]);
2352 else if(distance < 10.f)
2353 snprintf(buffer, sizeof(buffer), "%0.1f %s", distance,
2354 UNITS_ENUM_TEXT[_units]);
2356 snprintf(buffer, sizeof(buffer), "%0.f %s", distance,
2357 UNITS_ENUM_TEXT[_units]);
2358 pango_layout_set_text(_scale_layout, buffer, -1);
2360 pango_layout_get_pixel_size(_scale_layout, &width, NULL);
2362 /* Draw the layout itself. */
2363 gdk_draw_layout(_map_widget->window,
2364 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2365 _scale_rect.x + (_scale_rect.width - width) / 2,
2366 _scale_rect.y, _scale_layout);
2368 /* Draw little hashes on the ends. */
2369 gdk_draw_line(_map_widget->window,
2370 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2372 _scale_rect.y + _scale_rect.height / 2 - 4,
2374 _scale_rect.y + _scale_rect.height / 2 + 4);
2375 gdk_draw_line(_map_widget->window,
2376 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2378 _scale_rect.y + _scale_rect.height / 2,
2379 _scale_rect.x + (_scale_rect.width - width) / 2 - 4,
2380 _scale_rect.y + _scale_rect.height / 2);
2381 gdk_draw_line(_map_widget->window,
2382 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2383 _scale_rect.x + _scale_rect.width - 4,
2384 _scale_rect.y + _scale_rect.height / 2 - 4,
2385 _scale_rect.x + _scale_rect.width - 4,
2386 _scale_rect.y + _scale_rect.height / 2 + 4);
2387 gdk_draw_line(_map_widget->window,
2388 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2389 _scale_rect.x + _scale_rect.width - 4,
2390 _scale_rect.y + _scale_rect.height / 2,
2391 _scale_rect.x + (_scale_rect.width + width) / 2 + 4,
2392 _scale_rect.y + _scale_rect.height / 2);
2397 /* Draw the compass rose, if necessary. */
2401 gint offsetx, offsety;
2404 offsetx = _comprose_rect.x;
2405 offsety = _comprose_rect.y;
2407 gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 0, 12);
2408 points[0].x = offsetx + x + 0.5f; points[0].y = offsety + y + 0.5f;
2409 gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 20, 30);
2410 points[1].x = offsetx + x + 0.5f; points[1].y = offsety + y + 0.5f;
2411 gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 0, -45);
2412 points[2].x = offsetx + x + 0.5f; points[2].y = offsety + y + 0.5f;
2413 gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, -20, 30);
2414 points[3].x = offsetx + x + 0.5f; points[3].y = offsety + y + 0.5f;
2416 gdk_draw_polygon(_map_widget->window,
2417 _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2420 gdk_draw_polygon(_map_widget->window,
2421 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2422 FALSE, /* NOT FILLED */
2425 gdk_draw_layout(_map_widget->window,
2426 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2427 _comprose_rect.x - _comprose_rect.width / 2 + 1,
2428 _comprose_rect.y - _comprose_rect.height / 2 - 4,
2432 /* Draw a stopwatch if we're redrawing the map. */
2433 for(i = _redraw_count - 1; i >= 0; i--)
2436 _map_widget->window,
2437 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2440 _redraw_wait_bounds.x + i
2441 * HOURGLASS_SEPARATION,
2442 _redraw_wait_bounds.y,
2443 _redraw_wait_bounds.width,
2444 _redraw_wait_bounds.height,
2445 GDK_RGB_DITHER_NONE, 0, 0);
2448 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2453 latlon_cb_copy_clicked(GtkWidget *widget, LatlonDialog *lld) {
2456 snprintf(buffer, sizeof(buffer),
2458 gtk_label_get_text(GTK_LABEL(lld->lat)),
2459 gtk_label_get_text(GTK_LABEL(lld->lon)));
2461 gtk_clipboard_set_text(
2462 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), buffer, -1);
2466 latlon_cb_fmt_changed(GtkWidget *widget, LatlonDialog *lld) {
2469 fmt = gtk_combo_box_get_active(GTK_COMBO_BOX(lld->fmt_combo));
2472 gint old = _degformat; /* augh... */
2473 gchar buffer[LL_FMT_LEN];
2476 lat_format(lld->glat, buffer);
2477 gtk_label_set_label(GTK_LABEL(lld->lat), buffer);
2478 lon_format(lld->glon, buffer);
2479 gtk_label_set_label(GTK_LABEL(lld->lon), buffer);
2485 latlon_dialog(gdouble lat, gdouble lon)
2493 GtkWidget *cmb_format;
2494 GtkWidget *btn_copy = NULL;
2495 printf("%s()\n", __PRETTY_FUNCTION__);
2497 dialog = gtk_dialog_new_with_buttons(_("Show Position"),
2498 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
2499 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2502 /* Set the lat/lon strings. */
2503 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2504 table = gtk_table_new(5, 2, FALSE), TRUE, TRUE, 0);
2506 gtk_table_attach(GTK_TABLE(table),
2507 label = gtk_label_new(_("Lat")),
2508 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2509 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2510 gtk_table_attach(GTK_TABLE(table),
2511 txt_lat = gtk_label_new(""),
2512 1, 2, 0, 1, GTK_FILL, 0, 2, 4);
2513 gtk_misc_set_alignment(GTK_MISC(txt_lat), 1.f, 0.5f);
2515 gtk_table_attach(GTK_TABLE(table),
2516 label = gtk_label_new(_("Lon")),
2517 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2518 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2519 gtk_table_attach(GTK_TABLE(table),
2520 txt_lon = gtk_label_new(""),
2521 1, 2, 1, 2, GTK_FILL, 0, 2, 4);
2522 gtk_misc_set_alignment(GTK_MISC(txt_lon), 1.f, 0.5f);
2524 gtk_table_attach(GTK_TABLE(table),
2525 label = gtk_label_new(_("Format")),
2526 0, 1, 2, 3, GTK_FILL, 0, 2, 4);
2527 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2528 gtk_table_attach(GTK_TABLE(table),
2529 label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f),
2530 1, 2, 2, 3, GTK_FILL, 0, 2, 4);
2531 gtk_container_add(GTK_CONTAINER(label),
2532 cmb_format = gtk_combo_box_new_text());
2533 gtk_table_attach(GTK_TABLE(table),
2534 btn_copy = gtk_button_new_with_label(_("Copy")),
2535 0, 2, 3, 4, GTK_FILL, 0, 2, 4);
2539 gchar buffer[LL_FMT_LEN];
2541 lat_format(lat, buffer);
2542 gtk_label_set_label(GTK_LABEL(txt_lat), buffer);
2543 lat_format(lon, buffer);
2544 gtk_label_set_label(GTK_LABEL(txt_lon), buffer);
2547 /* Fill in formats */
2551 for(i = 0; i < DEG_FORMAT_ENUM_COUNT; i++) {
2552 gtk_combo_box_append_text(GTK_COMBO_BOX(cmb_format),
2553 DEG_FORMAT_ENUM_TEXT[i]);
2555 gtk_combo_box_set_active(GTK_COMBO_BOX(cmb_format), _degformat);
2559 /* setup cb context */
2560 lld.fmt_combo = cmb_format;
2566 /* Connect Signals */
2567 g_signal_connect(G_OBJECT(cmb_format), "changed",
2568 G_CALLBACK(latlon_cb_fmt_changed), &lld);
2569 g_signal_connect(G_OBJECT(btn_copy), "clicked",
2570 G_CALLBACK(latlon_cb_copy_clicked), &lld);
2572 gtk_widget_show_all(dialog);
2574 gtk_dialog_run(GTK_DIALOG(dialog));
2575 gtk_widget_hide(dialog);
2577 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2583 * This is a multi-purpose function for allowing the user to select a file
2584 * for either reading or writing. If chooser_action is
2585 * GTK_FILE_CHOOSER_ACTION_OPEN, then bytes_out and size_out must be
2586 * non-NULL. If chooser_action is GTK_FILE_CHOOSER_ACTION_SAVE, then
2587 * handle_out must be non-NULL. Either dir or file (or both) can be NULL.
2588 * This function returns TRUE if a file was successfully opened.
2591 display_open_file(GtkWindow *parent, gchar **bytes_out,
2592 GnomeVFSHandle **handle_out, gint *size_out,
2593 gchar **dir, gchar **file, GtkFileChooserAction chooser_action)
2596 gboolean success = FALSE;
2597 printf("%s()\n", __PRETTY_FUNCTION__);
2599 dialog = hildon_file_chooser_dialog_new(parent, chooser_action);
2602 gtk_file_chooser_set_current_folder_uri(
2603 GTK_FILE_CHOOSER(dialog), *dir);
2606 gtk_file_chooser_set_uri(
2607 GTK_FILE_CHOOSER(dialog), *file);
2608 if(chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE)
2610 /* Work around a bug in HildonFileChooserDialog. */
2611 gchar *basename = g_path_get_basename(*file);
2612 g_object_set(G_OBJECT(dialog), "autonaming", FALSE, NULL);
2613 gtk_file_chooser_set_current_name(
2614 GTK_FILE_CHOOSER(dialog), basename);
2619 gtk_widget_show_all(dialog);
2621 while(!success && gtk_dialog_run(GTK_DIALOG(dialog))==GTK_RESPONSE_OK)
2623 gchar *file_uri_str;
2624 GnomeVFSResult vfs_result;
2626 /* Get the selected filename. */
2627 file_uri_str = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
2629 if((chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN
2630 && (GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
2631 file_uri_str, size_out, bytes_out))))
2632 || (chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE
2633 && GNOME_VFS_OK != (vfs_result = gnome_vfs_create(
2634 handle_out, file_uri_str,
2635 GNOME_VFS_OPEN_WRITE, FALSE, 0664))))
2637 gchar buffer[BUFFER_SIZE];
2638 snprintf(buffer, sizeof(buffer),
2639 "%s:\n%s", chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN
2640 ? _("Failed to open file for reading")
2641 : _("Failed to open file for writing"),
2642 gnome_vfs_result_to_string(vfs_result));
2643 popup_error(dialog, buffer);
2648 g_free(file_uri_str);
2657 *dir = gtk_file_chooser_get_current_folder_uri(
2658 GTK_FILE_CHOOSER(dialog));
2661 /* If desired, save the file for later. */
2665 *file = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
2669 gtk_widget_destroy(dialog);
2671 vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, success);
2678 PangoContext *pango_context;
2679 PangoFontDescription *pango_font;
2681 printf("%s()\n", __PRETTY_FUNCTION__);
2683 /* Cache some pango and GCs for drawing. */
2684 pango_context = gtk_widget_get_pango_context(_map_widget);
2685 _scale_layout = pango_layout_new(pango_context);
2686 pango_font = pango_font_description_new();
2687 pango_font_description_set_size(pango_font, 12 * PANGO_SCALE);
2688 pango_layout_set_font_description(_scale_layout, pango_font);
2691 pango_context = gtk_widget_get_pango_context(_map_widget);
2692 _zoom_layout = pango_layout_new(pango_context);
2693 pango_font = pango_font_description_new();
2694 pango_font_description_set_size(pango_font, 12 * PANGO_SCALE);
2695 pango_layout_set_font_description(_zoom_layout, pango_font);
2696 pango_layout_set_alignment(_zoom_layout, PANGO_ALIGN_CENTER);
2699 pango_context = gtk_widget_get_pango_context(_map_widget);
2700 _comprose_layout = pango_layout_new(pango_context);
2701 pango_font = pango_font_description_new();
2702 pango_font_description_set_size(pango_font, 16 * PANGO_SCALE);
2703 pango_font_description_set_weight(pango_font, PANGO_WEIGHT_BOLD);
2704 pango_layout_set_font_description(_comprose_layout, pango_font);
2705 pango_layout_set_alignment(_comprose_layout, PANGO_ALIGN_CENTER);
2706 pango_layout_set_text(_comprose_layout, "N", -1);
2708 PangoRectangle rect;
2709 pango_layout_get_pixel_extents(_comprose_layout, &rect, NULL);
2710 _comprose_rect.width = rect.width + 3;
2711 _comprose_rect.height = rect.height + 3;
2715 _speed_limit_gc1 = gdk_gc_new (_map_widget->window);
2719 gdk_gc_set_rgb_fg_color(_speed_limit_gc1, &color);
2723 _speed_limit_gc2 = gdk_gc_new(_map_widget->window);
2724 gdk_gc_set_rgb_fg_color(_speed_limit_gc2, &color);
2725 pango_context = gtk_widget_get_pango_context(_map_widget);
2726 _speed_limit_layout = pango_layout_new(pango_context);
2727 pango_font = pango_font_description_new();
2728 pango_font_description_set_size(pango_font, 64 * PANGO_SCALE);
2729 pango_layout_set_font_description(_speed_limit_layout,
2731 pango_layout_set_alignment(_speed_limit_layout, PANGO_ALIGN_CENTER);
2733 /* draw_sat_info() */
2734 _sat_info_gc1 = gdk_gc_new(_map_widget->window);
2738 gdk_gc_set_rgb_fg_color(_sat_info_gc1, &color);
2741 color.blue = 0xffff;
2742 _sat_info_gc2 = gdk_gc_new(_map_widget->window);
2743 gdk_gc_set_rgb_fg_color(_sat_info_gc2, &color);
2744 pango_context = gtk_widget_get_pango_context(_map_widget);
2745 _sat_info_layout = pango_layout_new(pango_context);
2746 pango_font = pango_font_description_new();
2747 pango_font_description_set_family(pango_font,"Sans Serif");
2748 pango_font_description_set_size(pango_font, 8*PANGO_SCALE);
2749 pango_layout_set_font_description(_sat_info_layout, pango_font);
2750 pango_layout_set_alignment(_sat_info_layout, PANGO_ALIGN_CENTER);
2752 /* sat_panel_expose() */
2753 pango_context = gtk_widget_get_pango_context(_map_widget);
2754 _sat_panel_layout = pango_layout_new(pango_context);
2755 pango_font = pango_font_description_new();
2756 pango_font_description_set_family(pango_font,"Sans Serif");
2757 pango_font_description_set_size(pango_font, 14*PANGO_SCALE);
2758 pango_layout_set_font_description (_sat_panel_layout, pango_font);
2760 /* heading_panel_expose() */
2761 pango_context = gtk_widget_get_pango_context(_map_widget);
2762 _heading_panel_layout = pango_layout_new(pango_context);
2763 _heading_panel_fontdesc = pango_font_description_new();
2764 pango_font_description_set_family(_heading_panel_fontdesc, "Sans Serif");
2766 /* draw_sat_details() */
2767 pango_context = gtk_widget_get_pango_context(_map_widget);
2768 _sat_details_layout = pango_layout_new(pango_context);
2769 pango_font = pango_font_description_new();
2770 pango_font_description_set_family(pango_font,"Sans Serif");
2771 pango_font_description_set_size(pango_font, 10*PANGO_SCALE);
2772 pango_layout_set_font_description(_sat_details_layout,
2774 pango_layout_set_alignment(_sat_details_layout, PANGO_ALIGN_CENTER);
2776 /* sat_details_panel_expose() */
2777 pango_context = gtk_widget_get_pango_context(_map_widget);
2778 _sat_details_expose_layout = pango_layout_new(pango_context);
2779 pango_font = pango_font_description_new();
2780 pango_font_description_set_family(
2781 pango_font,"Sans Serif");
2782 pango_layout_set_alignment(_sat_details_expose_layout,
2783 PANGO_ALIGN_CENTER);
2784 pango_font_description_set_size(pango_font,
2786 pango_layout_set_font_description(_sat_details_expose_layout,
2789 /* Load the _redraw_wait_icon. */
2791 GError *error = NULL;
2792 gchar *icon_path = "/usr/share/icons/hicolor/scalable/hildon"
2793 "/maemo-mapper-wait.png";
2794 _redraw_wait_bounds.x = 0;
2795 _redraw_wait_bounds.y = 0;
2796 _redraw_wait_icon = gdk_pixbuf_new_from_file(icon_path, &error);
2797 if(!_redraw_wait_icon || error)
2799 g_printerr("Error parsing pixbuf: %s\n",
2800 error ? error->message : icon_path);
2801 _redraw_wait_bounds.width = 0;
2802 _redraw_wait_bounds.height = 0;
2803 _redraw_wait_icon = NULL;
2807 _redraw_wait_bounds.width
2808 = gdk_pixbuf_get_width(_redraw_wait_icon);
2809 _redraw_wait_bounds.height
2810 = gdk_pixbuf_get_height(_redraw_wait_icon);
2814 g_signal_connect(G_OBJECT(_map_widget), "configure_event",
2815 G_CALLBACK(map_cb_configure), NULL);
2817 g_signal_connect(G_OBJECT(_map_widget), "expose_event",
2818 G_CALLBACK(map_cb_expose), NULL);
2819 g_signal_connect(G_OBJECT(_sat_panel), "expose_event",
2820 G_CALLBACK(sat_panel_expose), NULL);
2821 g_signal_connect(G_OBJECT(_heading_panel), "expose_event",
2822 G_CALLBACK(heading_panel_expose), NULL);
2824 vprintf("%s(): return\n", __PRETTY_FUNCTION__);