]> git.itanic.dy.fi Git - maemo-mapper/blob - src/display.c
Added "Map Correction" feature, to address the rare problem of inaccurate
[maemo-mapper] / src / display.c
1 /*
2  * Copyright (C) 2006, 2007 John Costigan.
3  *
4  * POI and GPS-Info code originally written by Cezary Jackiewicz.
5  *
6  * Default map data provided by http://www.openstreetmap.org/
7  *
8  * This file is part of Maemo Mapper.
9  *
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.
14  *
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.
19  *
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/>.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #    include "config.h"
26 #endif
27
28 #define _GNU_SOURCE
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>
33
34 #ifndef LEGACY
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>
40 #else
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>
46 #endif
47
48 #include "types.h"
49 #include "data.h"
50 #include "defines.h"
51
52 #include "dbus-ifc.h"
53 #include "display.h"
54 #include "gdk-pixbuf-rotate.h"
55 #include "gps.h"
56 #include "maps.h"
57 #include "path.h"
58 #include "poi.h"
59 #include "settings.h"
60 #include "util.h"
61
62 #define VELVEC_SIZE_FACTOR (4)
63
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;
77
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;
89
90 static volatile gint _pending_replaces = 0;
91
92 /** Pango stuff. */
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;
108
109 #define SCALE_WIDTH (100)
110
111 static gboolean
112 speed_excess(void)
113 {
114     printf("%s()\n", __PRETTY_FUNCTION__);
115     if(!_speed_excess)
116         return FALSE;
117
118     hildon_play_system_sound(
119         "/usr/share/sounds/ui-information_note.wav");
120
121     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
122     return TRUE;
123 }
124
125 static void
126 speed_limit(void)
127 {
128     GdkGC *gc;
129     gfloat cur_speed;
130     gchar *buffer;
131     static gint x = 0, y = 0, width = 0, height = 0;
132     printf("%s()\n", __PRETTY_FUNCTION__);
133
134     cur_speed = _gps.speed * UNITS_CONVERT[_units];
135
136     if(cur_speed > _speed_limit)
137     {
138         gc = _speed_limit_gc1;
139         if(!_speed_excess)
140         {
141             _speed_excess = TRUE;
142             g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
143                     5000, (GSourceFunc)speed_excess, NULL, NULL);
144         }
145     }
146     else
147     {
148         gc = _speed_limit_gc2;
149         _speed_excess = FALSE;
150     }
151
152     /* remove previous number */
153     if (width != 0 && height != 0) {
154       gtk_widget_queue_draw_area (_map_widget,
155                                  x - 5,
156                                  y - 5,
157                                  width + 10,
158                                  height + 10);
159       gdk_window_process_all_updates();
160     }
161
162     buffer = g_strdup_printf("%0.0f", cur_speed);
163     pango_layout_set_text(_speed_limit_layout, buffer, -1);
164
165
166     pango_layout_get_pixel_size(_speed_limit_layout, &width, &height);
167
168     switch (_speed_location)
169     {
170         case SPEED_LOCATION_TOP_RIGHT:
171             x = _map_widget->allocation.width - 10 - width;
172             y = 5;
173             break;
174         case SPEED_LOCATION_BOTTOM_RIGHT:
175             x = _map_widget->allocation.width - 10 - width;
176             y = _map_widget->allocation.height - 10 - height;
177             break;
178         case SPEED_LOCATION_BOTTOM_LEFT:
179             x = 10;
180             y = _map_widget->allocation.height - 10 - height;
181             break;
182         default:
183             x = 10;
184             y = 10;
185             break;
186     }
187
188     gdk_draw_layout(_map_widget->window,
189         gc,
190         x, y,
191         _speed_limit_layout);
192     g_free(buffer);
193
194     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
195 }
196
197 gboolean
198 gps_display_details(void)
199 {
200     gchar *buffer, litbuf[16];
201     printf("%s()\n", __PRETTY_FUNCTION__);
202
203     if(_gps.fix < 2)
204     {
205         /* no fix no fun */
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), " --:--:-- ");
212     }
213     else
214     {
215         gfloat speed = _gps.speed * UNITS_CONVERT[_units];
216
217         /* latitude */
218         lat_format(_gps.lat, litbuf);
219         gtk_label_set_label(GTK_LABEL(_sdi_lat), litbuf);
220
221         /* longitude */
222         lon_format(_gps.lon, litbuf);
223         gtk_label_set_label(GTK_LABEL(_sdi_lon), litbuf);
224
225         /* speed */
226         switch(_units)
227         {
228             case UNITS_MI:
229                 buffer = g_strdup_printf("%.1f mph", speed);
230                 break;
231             case UNITS_NM:
232                 buffer = g_strdup_printf("%.1f kn", speed);
233                 break;
234             default:
235                 buffer = g_strdup_printf("%.1f km/h", speed);
236                 break;
237         }
238         gtk_label_set_label(GTK_LABEL(_sdi_spd), buffer);
239         g_free(buffer);
240
241         /* altitude */
242         switch(_units)
243         {
244             case UNITS_MI:
245             case UNITS_NM:
246                 buffer = g_strdup_printf("%d ft",
247                         (gint)(_pos.altitude * 3.2808399f));
248                 break;
249             default:
250                 buffer = g_strdup_printf("%d m", _pos.altitude);
251                 break;
252         }
253         gtk_label_set_label(GTK_LABEL(_sdi_alt), buffer);
254         g_free(buffer);
255
256         /* heading */
257         buffer = g_strdup_printf("%0.0f°", _gps.heading);
258         gtk_label_set_label(GTK_LABEL(_sdi_hea), buffer);
259         g_free(buffer);
260
261         /* local time */
262         strftime(litbuf, 15, "%X", localtime(&_pos.time));
263         gtk_label_set_label(GTK_LABEL(_sdi_tim), litbuf);
264     }
265
266     /* Sat in view */
267     buffer = g_strdup_printf("%d", _gps.satinview);
268     gtk_label_set_label(GTK_LABEL(_sdi_vie), buffer);
269     g_free(buffer);
270
271     /* Sat in use */
272     buffer = g_strdup_printf("%d", _gps.satinuse);
273     gtk_label_set_label(GTK_LABEL(_sdi_use), buffer);
274     g_free(buffer);
275
276     /* fix */
277     switch(_gps.fix)
278     {
279         case 2:
280         case 3: buffer = g_strdup_printf("%dD fix", _gps.fix); break;
281         default: buffer = g_strdup_printf("nofix"); break;
282     }
283     gtk_label_set_label(GTK_LABEL(_sdi_fix), buffer);
284     g_free(buffer);
285
286     if(_gps.fix == 1)
287         buffer = g_strdup("none");
288     else
289     {
290         switch (_gps.fixquality)
291         {
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;
301         }
302     }
303     gtk_label_set_label(GTK_LABEL(_sdi_fqu), buffer);
304     g_free(buffer);
305
306     /* max speed */
307     {
308         gfloat maxspeed = _gps.maxspeed * UNITS_CONVERT[_units];
309
310         /* speed */
311         switch(_units)
312         {
313             case UNITS_MI:
314                 buffer = g_strdup_printf("%.1f mph", maxspeed);
315                 break;
316             case UNITS_NM:
317                 buffer = g_strdup_printf("%.1f kn", maxspeed);
318                 break;
319             default:
320                 buffer = g_strdup_printf("%.1f km/h", maxspeed);
321                 break;
322         }
323         gtk_label_set_label(GTK_LABEL(_sdi_msp), buffer);
324         g_free(buffer);
325     }
326
327     /* refresh sat panel */
328     gtk_widget_queue_draw_area(GTK_WIDGET(_sat_details_panel),
329         0, 0,
330         _sat_details_panel->allocation.width,
331         _sat_details_panel->allocation.height);
332
333     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
334     return TRUE;
335 }
336
337 void
338 gps_display_data(void)
339 {
340     gchar *buffer, litbuf[LL_FMT_LEN];
341     printf("%s()\n", __PRETTY_FUNCTION__);
342
343     if(_gps.fix < 2)
344     {
345         /* no fix no fun */
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), " --:--:-- ");
351     }
352     else
353     {
354         gfloat speed = _gps.speed * UNITS_CONVERT[_units];
355
356         /* latitude */
357         lat_format(_gps.lat, litbuf);
358         gtk_label_set_label(GTK_LABEL(_text_lat), litbuf);
359
360         /* longitude */
361         lon_format(_gps.lon, litbuf);
362         gtk_label_set_label(GTK_LABEL(_text_lon), litbuf);
363
364         /* speed */
365         switch(_units)
366         {
367             case UNITS_MI:
368                 buffer = g_strdup_printf("Spd: %.1f mph", speed);
369                 break;
370             case UNITS_NM:
371                 buffer = g_strdup_printf("Spd: %.1f kn", speed);
372                 break;
373             default:
374                 buffer = g_strdup_printf("Spd: %.1f km/h", speed);
375                 break;
376         }
377         gtk_label_set_label(GTK_LABEL(_text_speed), buffer);
378         g_free(buffer);
379
380         /* altitude */
381         switch(_units)
382         {
383             case UNITS_MI:
384             case UNITS_NM:
385                 buffer = g_strdup_printf("Alt: %d ft",
386                         (gint)(_pos.altitude * 3.2808399f));
387                 break;
388             default:
389                 buffer = g_strdup_printf("Alt: %d m", _pos.altitude);
390         }
391         gtk_label_set_label(GTK_LABEL(_text_alt), buffer);
392         g_free(buffer);
393
394         /* local time */
395         strftime(litbuf, 15, "%X", localtime(&_pos.time));
396         gtk_label_set_label(GTK_LABEL(_text_time), litbuf);
397     }
398
399     /* refresh sat panel */
400     gtk_widget_queue_draw_area(GTK_WIDGET(_sat_panel),
401         0, 0,
402         _sat_panel->allocation.width,
403         _sat_panel->allocation.height);
404
405     /* refresh heading panel*/
406     gtk_widget_queue_draw_area(GTK_WIDGET(_heading_panel),
407         0, 0,
408         _heading_panel->allocation.width,
409         _heading_panel->allocation.height);
410
411     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
412     return;
413 }
414
415 void
416 gps_hide_text(void)
417 {
418     printf("%s()\n", __PRETTY_FUNCTION__);
419
420     /* Clear gps data */
421     _gps.fix = 1;
422     _gps.satinuse = 0;
423     _gps.satinview = 0;
424
425     if(_gps_info)
426         gps_display_data();
427
428     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
429 }
430
431 void
432 gps_show_info(void)
433 {
434     printf("%s()\n", __PRETTY_FUNCTION__);
435
436     if(_gps_info && _enable_gps)
437         gtk_widget_show_all(GTK_WIDGET(_gps_widget));
438     else
439     {
440         gps_hide_text();
441         gtk_widget_hide(GTK_WIDGET(_gps_widget));
442     }
443
444     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
445 }
446
447 static void
448 draw_sat_info(GtkWidget *widget, gint x0, gint y0,
449         gint width, gint height, gboolean showsnr)
450 {
451     GdkGC *gc;
452     gint step, i, j, snr_height, bymargin, xoffset, yoffset;
453     gint x, y, x1, y1;
454     gchar *tmp = NULL;
455     printf("%s()\n", __PRETTY_FUNCTION__);
456
457     xoffset = x0;
458     yoffset = y0;
459     /* Bootom margin - 12% */
460     bymargin = height * 0.88f;
461
462     /* Bottom line */
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);
471
472     if(_gps.satinview > 0 )
473     {
474         /* Left margin - 5pix, Right margin - 5pix */
475         step = (width - 10) / _gps.satinview;
476
477         for(i = 0; i < _gps.satinview; i++)
478         {
479             /* Sat used or not */
480             gc = _sat_info_gc1;
481             for(j = 0; j < _gps.satinuse ; j++)
482             {
483                 if(_gps.satforfix[j] == _gps_sat[i].prn)
484                 {
485                     gc = _sat_info_gc2;
486                     break;
487                 }
488             }
489
490             x = 5 + i * step;
491             snr_height = _gps_sat[i].snr * height * 0.78f / 100;
492             y = height * 0.1f + (height * 0.78f - snr_height);
493
494             /* draw sat rectangle... */
495             gdk_draw_rectangle(widget->window,
496                     gc,
497                     TRUE,
498                     xoffset + x,
499                     yoffset + y,
500                     step - 2,
501                     snr_height);
502
503             if(showsnr && _gps_sat[i].snr > 0)
504             {
505                 /* ...snr.. */
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,
512                     yoffset + y - 15,
513                     _sat_info_layout);
514                 g_free(tmp);
515             }
516
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,
525                 _sat_info_layout);
526             g_free(tmp);
527         }
528     }
529
530     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
531     return;
532 }
533
534 static void
535 draw_sat_details(GtkWidget *widget, gint x0, gint y0,
536         gint width, gint height)
537 {
538     gint i, j, x, y, size, halfsize, xoffset, yoffset;
539     gint x1, y1;
540     gfloat tmp;
541     GdkColor color;
542     GdkGC *gc1, *gc2, *gc3, *gc;
543     gchar *buffer = NULL;
544     printf("%s()\n", __PRETTY_FUNCTION__);
545
546     size = MIN(width, height);
547     halfsize = size/2;
548     if(width > height)
549     {
550         xoffset = x0 + (width - height - 10) / 2;
551         yoffset = y0 + 5;
552     }
553     else
554     {
555         xoffset = x0 + 5;
556         yoffset = y0 + (height - width - 10) / 2;
557     }
558
559     /* 90 */
560     gdk_draw_arc(widget->window,
561             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
562             FALSE,
563             xoffset + 2, yoffset + 2, size - 4, size - 4,
564             0, 64 * 360);
565
566     /* 60 */
567     gdk_draw_arc(widget->window,
568             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
569             FALSE,
570             xoffset + size/6, yoffset + size/6,
571             size/6*4, size/6*4,
572             0, 64 * 360);
573
574     /* 30 */
575     gdk_draw_arc(widget->window,
576             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
577             FALSE,
578             xoffset + size/6*2, yoffset + size/6*2,
579             size/6*2, size/6*2,
580             0, 64 * 360);
581
582     gint line[12] = {0,30,60,90,120,150,180,210,240,270,300,330};
583
584     for(i = 0; i < 6; i++)
585     {
586         /* line */
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));
594     }
595
596     for(i = 0; i < 12; i++)
597     {
598         tmp = deg2rad(line[i]);
599         /* azimuth */
600         if(line[i] == 0)
601             buffer = g_strdup_printf("N");
602         else
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);
611         g_free(buffer);
612     }
613
614     /* elevation 30 */
615     tmp = deg2rad(30);
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);
624     g_free(buffer);
625
626     /* elevation 60 */
627     tmp = deg2rad(30);
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);
636     g_free(buffer);
637
638     color.red = 0;
639     color.green = 0;
640     color.blue = 0;
641     gc1 = gdk_gc_new (widget->window);
642     gdk_gc_set_rgb_fg_color (gc1, &color);
643
644     color.red = 0;
645     color.green = 0;
646     color.blue = 0xffff;
647     gc2 = gdk_gc_new (widget->window);
648     gdk_gc_set_rgb_fg_color (gc2, &color);
649
650     color.red = 0xffff;
651     color.green = 0xffff;
652     color.blue = 0xffff;
653     gc3 = gdk_gc_new (widget->window);
654     gdk_gc_set_rgb_fg_color (gc3, &color);
655
656     for(i = 0; i < _gps.satinview; i++)
657     {
658         /* Sat used or not */
659         gc = gc1;
660         for(j = 0; j < _gps.satinuse ; j++)
661         {
662             if(_gps.satforfix[j] == _gps_sat[i].prn)
663             {
664                 gc = gc2;
665                 break;
666             }
667         }
668
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);
674
675         gdk_draw_arc (widget->window,
676             gc, TRUE,
677             x - 10, y - 10, 20, 20,
678             0, 64 * 360);
679
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,
684             gc3,
685             x - x1/2,
686             y - y1/2,
687             _sat_details_layout);
688         g_free(buffer);
689     }
690     g_object_unref (gc1);
691     g_object_unref (gc2);
692     g_object_unref (gc3);
693
694     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
695     return;
696 }
697
698
699 static gboolean
700 sat_details_panel_expose(GtkWidget *widget, GdkEventExpose *event)
701 {
702     gint width, height, x, y;
703     gchar *buffer = NULL;
704     printf("%s()\n", __PRETTY_FUNCTION__);
705
706     width = widget->allocation.width;
707     height = widget->allocation.height * 0.9;
708
709     draw_sat_info(widget, 0, 0, width/2, height, TRUE);
710     draw_sat_details(widget, width/2, 0, width/2, height);
711
712     buffer = g_strdup_printf(
713         "%s: %d; %s: %d",
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)],
720         10,
721         height*0.9 + 10,
722         _sat_details_expose_layout);
723     g_free(buffer);
724
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)],
730         (width/8) - x/2,
731         (height/6) - y/2,
732         _sat_details_expose_layout);
733     g_free(buffer);
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)],
739         (width/8) - x/2,
740         (height/6) - y/2 + 20,
741         _sat_details_expose_layout);
742     g_free(buffer);
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)],
748         (width/8) - x/2,
749         (height/6) - y/2 + 40,
750         _sat_details_expose_layout);
751     g_free(buffer);
752
753     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
754     return TRUE;
755 }
756
757 void
758 gps_details(void)
759 {
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__);
765
766     if(dialog == NULL)
767     {
768         dialog = gtk_dialog_new_with_buttons(_("GPS Details"),
769                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
770                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
771                 NULL);
772
773         gtk_window_set_default_size(GTK_WINDOW(dialog), 600, 300);
774
775         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
776                 notebook = gtk_notebook_new(), TRUE, TRUE, 0);
777
778         /* textual info */
779         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
780                 table = gtk_table_new(4, 6, FALSE),
781                 label = gtk_label_new(_("GPS Information")));
782
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),
787                 _sat_details_panel,
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);
791
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);
800
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);
809
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);
818
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);
827
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);
836
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);
845
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);
854
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);
863
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);
872
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);
881
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);
890     }
891
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)))
896     {
897         _satdetails_on = FALSE;
898         break;
899     }
900     gtk_widget_hide(dialog);
901
902     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
903 }
904
905
906 /**
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.
910  */
911 void
912 map_render_segment(GdkGC *gc_norm, GdkGC *gc_alt,
913         gint unitx1, gint unity1, gint unitx2, gint unity2)
914 {
915     /* vprintf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
916             unitx1, unity1, unitx2, unity2); */
917
918     if(!unity1)
919     {
920         gint x2, y2;
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))
924         {
925             gdk_draw_arc(_map_pixmap, gc_alt,
926                     FALSE, /* FALSE: not filled. */
927                     x2 - _draw_width,
928                     y2 - _draw_width,
929                     2 * _draw_width,
930                     2 * _draw_width,
931                     0, /* start at 0 degrees. */
932                     360 * 64);
933         }
934     }
935     else if(!unity2)
936     {
937         gint x1, y1;
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))
941         {
942             gdk_draw_arc(_map_pixmap, gc_alt,
943                     FALSE, /* FALSE: not filled. */
944                     x1 - _draw_width,
945                     y1 - _draw_width,
946                     2 * _draw_width,
947                     2 * _draw_width,
948                     0, /* start at 0 degrees. */
949                     360 * 64);
950         }
951     }
952     else
953     {
954         gint x1, y1, x2, y2;
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);
963     }
964
965     /* vprintf("%s(): return\n", __PRETTY_FUNCTION__); */
966 }
967
968 /**
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
973  * redrawn.
974  */
975 static void
976 map_render_path(Path *path, GdkGC **gc)
977 {
978     Point *curr;
979     WayPoint *wcurr;
980     printf("%s()\n", __PRETTY_FUNCTION__);
981
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. */
985
986     /* else there is a route to draw. */
987     for(curr = path->head, wcurr = path->whead; curr++ != path->tail; )
988     {
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);
992
993         /* Now, check if curr is a waypoint. */
994         if(wcurr <= path->wtail && wcurr->point == curr)
995         {
996             gint x1, y1;
997             unit2buf(wcurr->point->unitx, wcurr->point->unity, x1, y1);
998             gdk_draw_arc(_map_pixmap,
999                     gc[1],
1000                     FALSE, /* FALSE: not filled. */
1001                     x1 - _draw_width,
1002                     y1 - _draw_width,
1003                     2 * _draw_width,
1004                     2 * _draw_width,
1005                     0, /* start at 0 degrees. */
1006                     360 * 64);
1007             wcurr++;
1008         }
1009     }
1010
1011     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1012 }
1013
1014 void
1015 map_render_paths()
1016 {
1017     printf("%s()\n", __PRETTY_FUNCTION__);
1018
1019     if((_show_paths & ROUTES_MASK) && _route.head != _route.tail)
1020     {
1021         WayPoint *next_way;
1022         map_render_path(&_route, _gc + COLORABLE_ROUTE);
1023
1024         next_way = path_get_next_way();
1025
1026         /* Now, draw the next waypoint on top of all other waypoints. */
1027         if(next_way)
1028         {
1029             gint x1, y1;
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. */
1035                     x1 - _draw_width,
1036                     y1 - _draw_width,
1037                     2 * _draw_width,
1038                     2 * _draw_width,
1039                     0, /* start at 0 degrees. */
1040                     360 * 64);
1041         }
1042     }
1043     if(_show_paths & TRACKS_MASK)
1044         map_render_path(&_track, _gc + COLORABLE_TRACK);
1045
1046     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1047 }
1048
1049 /**
1050  * Update all GdkGC objects to reflect the current _draw_width.
1051  */
1052 #define UPDATE_GC(gc) \
1053     gdk_gc_set_line_attributes(gc, \
1054             _draw_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
1055 void
1056 update_gcs()
1057 {
1058     gint i;
1059     printf("%s()\n", __PRETTY_FUNCTION__);
1060
1061     for(i = 0; i < COLORABLE_ENUM_COUNT; i++)
1062     {
1063         gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color[i]);
1064         if(_gc[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);
1070     }
1071     
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);
1079
1080     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1081 }
1082
1083 /**
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.
1087  */
1088 gboolean
1089 window_present()
1090 {
1091     static gint been_here = 0;
1092     static gint done_here = 0;
1093     printf("%s()\n", __PRETTY_FUNCTION__);
1094
1095     if(!been_here++)
1096     {
1097         /* Set connection state first, to avoid going into this if twice. */
1098         if(_is_first_time)
1099         {
1100             GtkWidget *confirm;
1101
1102             gtk_window_present(GTK_WINDOW(_window));
1103
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."));
1108
1109             if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
1110             {
1111 #ifndef LEGACY
1112                 hildon_help_show(_osso, HELP_ID_INTRO, 0);
1113 #else
1114                 ossohelp_show(_osso, HELP_ID_INTRO, 0);
1115 #endif
1116             }
1117             gtk_widget_destroy(confirm);
1118
1119             /* Present the settings dialog. */
1120             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."));
1125
1126             /* Present the repository dialog. */
1127             repoman_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)))
1134             {
1135                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
1136                             _menu_maps_auto_download_item), TRUE);
1137             }
1138             gtk_widget_destroy(confirm);
1139         }
1140
1141         /* Connect to receiver. */
1142         if(_enable_gps)
1143             rcvr_connect();
1144
1145         ++done_here; /* Don't ask... */
1146     }
1147     if(done_here)
1148     {
1149         gtk_window_present(GTK_WINDOW(_window));
1150         g_timeout_add_full(G_PRIORITY_HIGH_IDLE,
1151                 250, (GSourceFunc)banner_reset, NULL, NULL);
1152     }
1153
1154     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1155     return FALSE;
1156 }
1157
1158 /**
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().
1161  */
1162 static void
1163 map_set_mark()
1164 {
1165     gfloat sqrt_speed, tmp, vel_offset_devx, vel_offset_devy;
1166     printf("%s()\n", __PRETTY_FUNCTION__);
1167
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,
1171             _map_rotate_matrix,
1172             sqrt_speed * sinf(tmp), -sqrt_speed * cosf(tmp));
1173
1174     unit2buf(_pos.unitx, _pos.unity, _mark_bufx1, _mark_bufy1);
1175
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);
1178
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);
1183
1184     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1185 }
1186
1187
1188 /**
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.
1191  */
1192 void
1193 map_force_redraw()
1194 {
1195     printf("%s()\n", __PRETTY_FUNCTION__);
1196
1197     gdk_draw_pixbuf(
1198             _map_pixmap,
1199             _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
1200             _map_pixbuf,
1201             0, 0,
1202             0, 0,
1203             _view_width_pixels, _view_height_pixels,
1204             GDK_RGB_DITHER_NONE, 0, 0);
1205
1206     MACRO_MAP_RENDER_DATA();
1207     MACRO_QUEUE_DRAW_AREA();
1208
1209     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1210 }
1211
1212 Point
1213 map_calc_new_center(gint zoom)
1214 {
1215     Point new_center;
1216     printf("%s()\n", __PRETTY_FUNCTION__);
1217
1218     switch(_center_mode)
1219     {
1220         case CENTER_LEAD:
1221         {
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] -
1228                                 (_center_rotate ? 0
1229                              : (_next_map_rotate_angle
1230                                  - (gint)(_gps.heading)))))));
1231             gfloat lead_pixels = 0.0025f
1232                 * pixel2zunit((gint)screen_pixels, zoom)
1233                 * _lead_ratio
1234                 * VELVEC_SIZE_FACTOR
1235                 * (_lead_is_fixed ? 7 : sqrtf(_gps.speed));
1236
1237             new_center.unitx = _pos.unitx + (gint)(lead_pixels * sinf(tmp));
1238             new_center.unity = _pos.unity - (gint)(lead_pixels * cosf(tmp));
1239             break;
1240         }
1241         case CENTER_LATLON:
1242             new_center.unitx = _pos.unitx;
1243             new_center.unity = _pos.unity;
1244             break;
1245         default:
1246             new_center.unitx = _next_center.unitx;
1247             new_center.unity = _next_center.unity;
1248     }
1249
1250     vprintf("%s(): return (%d, %d)\n", __PRETTY_FUNCTION__,
1251             new_center.unitx, new_center.unity);
1252     return new_center;
1253 }
1254
1255 /**
1256  * Center the view on the given unitx/unity.
1257  */
1258 void
1259 map_center_unit_full(Point new_center,
1260         gint zoom, gint rotate_angle)
1261 {
1262     MapRenderTask *mrt;
1263     printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
1264             new_center.unitx, new_center.unity);
1265
1266     if(!_mouse_is_down)
1267     {
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);
1271
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;
1281
1282
1283         gtk_widget_queue_draw_area(
1284                 _map_widget,
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);
1290
1291         ++_redraw_count;
1292
1293         g_thread_pool_push(_mrt_thread_pool, mrt, NULL);
1294     }
1295
1296     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1297 }
1298
1299 void
1300 map_center_unit(Point new_center)
1301 {
1302     map_center_unit_full(new_center, _next_zoom,
1303         _center_mode > 0 && _center_rotate
1304             ? _gps.heading : _next_map_rotate_angle);
1305 }
1306
1307 void
1308 map_rotate(gint rotate_angle)
1309 {
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);
1314
1315     map_center_unit_full(map_calc_new_center(_next_zoom), _next_zoom,
1316         (_next_map_rotate_angle + rotate_angle) % 360);
1317 }
1318
1319 void
1320 map_center_zoom(gint zoom)
1321 {
1322     map_center_unit_full(map_calc_new_center(zoom), zoom,
1323         _center_mode > 0 && _center_rotate
1324             ? _gps.heading : _next_map_rotate_angle);
1325 }
1326
1327 /**
1328  * Pan the view by the given number of units in the X and Y directions.
1329  */
1330 void
1331 map_pan(gint delta_unitx, gint delta_unity)
1332 {
1333     Point new_center;
1334     printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, delta_unitx, delta_unity);
1335
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);
1344
1345     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1346 }
1347
1348 /**
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.
1353  */
1354 void
1355 map_move_mark()
1356 {
1357     printf("%s()\n", __PRETTY_FUNCTION__);
1358
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,
1363             _mark_width,
1364             _mark_height);
1365     map_set_mark();
1366     gtk_widget_queue_draw_area(_map_widget,
1367             _mark_minx + _map_offset_devx,
1368             _mark_miny + _map_offset_devy,
1369             _mark_width,
1370             _mark_height);
1371
1372     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1373 }
1374
1375 /**
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.
1378  */
1379 void
1380 map_refresh_mark(gboolean force_redraw)
1381 {
1382     printf("%s()\n", __PRETTY_FUNCTION__);
1383
1384     gint new_center_devx, new_center_devy;
1385
1386     Point new_center = map_calc_new_center(_next_zoom);
1387
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))))))
1399     {
1400         map_move_mark();
1401         map_center_unit(new_center);
1402     }
1403     else
1404     {
1405         /* We're not changing the view - just move the mark. */
1406         map_move_mark();
1407     }
1408
1409     /* Draw speed info */
1410     if(_speed_limit_on)
1411         speed_limit();
1412
1413     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1414 }
1415
1416 gboolean
1417 map_download_refresh_idle(MapUpdateTask *mut)
1418 {
1419     vprintf("%s(%p, %d, %d, %d)\n", __PRETTY_FUNCTION__, mut,
1420             mut->zoom, mut->tilex, mut->tiley);
1421
1422     /* Test if download succeeded (only if retries != 0). */
1423     if(mut->pixbuf && mut->repo == _curr_repo)
1424     {
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)
1429         {
1430             gfloat destx, desty;
1431             gint boundx, boundy, width, height;
1432
1433             pixel2buf(
1434                     tile2pixel(mut->tilex << zoff)
1435                         + ((TILE_SIZE_PIXELS << zoff) >> 1),
1436                     tile2pixel(mut->tiley << zoff)
1437                         + ((TILE_SIZE_PIXELS << zoff) >> 1),
1438                     destx, desty);
1439
1440             /* Multiply the matrix to cause blitting. */
1441             if(zoff)
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),
1448                         _map_rotate_matrix,
1449                         mut->pixbuf,
1450                         TILE_SIZE_PIXELS / 2,
1451                         TILE_SIZE_PIXELS / 2,
1452                         TILE_SIZE_PIXELS,
1453                         TILE_SIZE_PIXELS,
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... */
1458             if(zoff)
1459                 gdk_pixbuf_rotate_matrix_mult_number(
1460                         _map_rotate_matrix, 1.f / (1 << zoff));
1461
1462             if(width * height)
1463             {
1464                 gdk_draw_pixbuf(
1465                         _map_pixmap,
1466                         _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
1467                         _map_pixbuf,
1468                         boundx, boundy,
1469                         boundx, boundy,
1470                         width, height,
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);
1475             }
1476         }
1477         g_object_unref(mut->pixbuf);
1478     }
1479     else if(mut->vfs_result != GNOME_VFS_OK)
1480     {
1481         _dl_errors++;
1482     }
1483
1484     if(++_curr_download == _num_downloads)
1485     {
1486         if(_download_banner)
1487         {
1488             gtk_widget_destroy(_download_banner);
1489             _download_banner = NULL;
1490         }
1491         _num_downloads = _curr_download = 0;
1492         g_thread_pool_stop_unused_threads();
1493 #ifndef MAPDB_SQLITE
1494         if(_curr_repo->db)
1495             gdbm_sync(_curr_repo->db);
1496 #endif
1497         if(_dl_errors)
1498         {
1499             gchar buffer[BUFFER_SIZE];
1500             snprintf(buffer, sizeof(buffer), "%d %s", _dl_errors,
1501                     _("maps failed to download."));
1502             MACRO_BANNER_SHOW_INFO(_window, buffer);
1503             _dl_errors = 0;
1504         }
1505         else if(mut->update_type != MAP_UPDATE_AUTO)
1506         {
1507             /* Update the map. */
1508             map_refresh_mark(TRUE);
1509         }
1510     }
1511     else if(_download_banner)
1512     {
1513         hildon_banner_set_fraction(HILDON_BANNER(_download_banner),
1514                 _curr_download / (double)_num_downloads);
1515     }
1516
1517     g_mutex_lock(_mut_priority_mutex);
1518     g_hash_table_remove(_mut_exists_table, mut);
1519     g_mutex_unlock(_mut_priority_mutex);
1520
1521     g_slice_free(MapUpdateTask, mut);
1522
1523     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1524     return FALSE;
1525 }
1526
1527 /**
1528  * Set the current zoom level.  If the given zoom level is the same as the
1529  * current zoom level, or if the new zoom is invalid
1530  * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
1531  */
1532 void
1533 map_set_zoom(gint new_zoom)
1534 {
1535     printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom);
1536
1537     /* This if condition also checks for new_zoom >= 0. */
1538     if((unsigned)new_zoom > MAX_ZOOM)
1539         return;
1540
1541     map_center_zoom(new_zoom / _curr_repo->view_zoom_steps
1542                      * _curr_repo->view_zoom_steps);
1543
1544     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1545 }
1546
1547 static gboolean
1548 map_replace_pixbuf_idle(MapRenderTask *mrt)
1549 {
1550     printf("%s()\n", __PRETTY_FUNCTION__);
1551
1552     if(!--_pending_replaces && !_mouse_is_down
1553             && mrt->screen_width_pixels == _view_width_pixels
1554             && mrt->screen_height_pixels == _view_height_pixels)
1555     {
1556         g_object_unref(_map_pixbuf);
1557         _map_pixbuf = mrt->pixbuf;
1558
1559         if(_center.unitx != mrt->new_center.unitx
1560                 || _center.unity != mrt->new_center.unity
1561                 || _zoom != mrt->zoom
1562                 || _map_rotate_angle != mrt->rotate_angle)
1563         {
1564             dbus_ifc_fire_view_position_changed(
1565                     mrt->new_center, mrt->zoom, mrt->rotate_angle);
1566         }
1567
1568         _center = mrt->new_center;
1569         _zoom = mrt->zoom;
1570         _map_rotate_angle = mrt->rotate_angle;
1571
1572         gdk_pixbuf_rotate_matrix_fill_for_rotation(
1573                 _map_rotate_matrix,
1574                 deg2rad(ROTATE_DIR_ENUM_DEGREES[_rotate_dir]
1575                     - _map_rotate_angle));
1576         gdk_pixbuf_rotate_matrix_fill_for_rotation(
1577                 _map_reverse_matrix,
1578                 deg2rad(_map_rotate_angle
1579                     - ROTATE_DIR_ENUM_DEGREES[_rotate_dir]));
1580
1581         --_redraw_count;
1582
1583         _map_offset_devx = 0;
1584         _map_offset_devy = 0;
1585
1586         map_set_mark();
1587         map_force_redraw();
1588     }
1589     else
1590     {
1591         /* Ignore this new pixbuf. We have newer ones coming. */
1592         g_object_unref(mrt->pixbuf);
1593         --_redraw_count;
1594     }
1595
1596     g_slice_free(MapRenderTask, mrt);
1597
1598     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1599     return FALSE;
1600 }
1601
1602 gboolean
1603 thread_render_map(MapRenderTask *mrt)
1604 {
1605     gfloat matrix[4];
1606     gint start_tilex, start_tiley, stop_tilex, stop_tiley;
1607     gint x = 0, y, num_tilex, num_tiley;
1608     gint diag_halflength_units;
1609     gfloat angle_rad;
1610     gint tile_rothalf_pixels;
1611     gint curr_tile_to_draw, num_tiles_to_draw;
1612     gfloat *tile_dev;
1613     ThreadLatch *refresh_latch = NULL;
1614     gint cache_amount;
1615     static gint8 auto_download_batch_id = INT8_MIN;
1616     printf("%s(%d, %d, %d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
1617             mrt->screen_width_pixels, mrt->screen_height_pixels,
1618             mrt->new_center.unitx, mrt->new_center.unity, mrt->zoom,
1619             mrt->rotate_angle);
1620
1621     /* If there are more render tasks in the queue, skip this one. */
1622     if(g_thread_pool_unprocessed(_mrt_thread_pool))
1623     {
1624         g_slice_free(MapRenderTask, mrt);
1625         --_redraw_count;
1626         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1627         return FALSE;
1628     }
1629
1630     angle_rad = deg2rad(ROTATE_DIR_ENUM_DEGREES[_rotate_dir]
1631             - mrt->rotate_angle);
1632
1633     gdk_pixbuf_rotate_matrix_fill_for_rotation(matrix, angle_rad);
1634
1635     /* Determine (roughly) the tiles we might have to process.
1636      * Basically, we take the center unit and subtract the maximum dimension
1637      * of the screen plus the maximum additional pixels of a rotated tile.
1638      */
1639     tile_rothalf_pixels = MAX(
1640             fabsf(TILE_HALFDIAG_PIXELS * sinf((PI / 4) - angle_rad)),
1641             fabsf(TILE_HALFDIAG_PIXELS * cosf((PI / 4) - angle_rad)));
1642
1643     mrt->zoom = _next_zoom;
1644
1645     if(mrt->repo->type != REPOTYPE_NONE && mrt->repo->db)
1646         cache_amount = _auto_download_precache;
1647     else
1648         cache_amount = 1; /* No cache. */
1649
1650     diag_halflength_units = pixel2zunit(TILE_HALFDIAG_PIXELS
1651         + MAX(mrt->screen_width_pixels, mrt->screen_height_pixels) / 2,
1652         mrt->zoom);
1653     start_tilex = unit2ztile(
1654             mrt->new_center.unitx - diag_halflength_units
1655             + _map_correction_unitx, mrt->zoom);
1656     start_tilex = MAX(start_tilex - (cache_amount - 1), 0);
1657     start_tiley = unit2ztile(
1658             mrt->new_center.unity - diag_halflength_units
1659             + _map_correction_unity, mrt->zoom);
1660     start_tiley = MAX(start_tiley - (cache_amount - 1), 0);
1661     stop_tilex = unit2ztile(mrt->new_center.unitx + diag_halflength_units
1662             + _map_correction_unitx, mrt->zoom);
1663     stop_tilex = MIN(stop_tilex + (cache_amount - 1),
1664             unit2ztile(WORLD_SIZE_UNITS, mrt->zoom));
1665     stop_tiley = unit2ztile(mrt->new_center.unity + diag_halflength_units
1666             + _map_correction_unity, mrt->zoom);
1667     stop_tiley = MIN(stop_tiley + (cache_amount - 1),
1668             unit2ztile(WORLD_SIZE_UNITS, mrt->zoom));
1669
1670     num_tilex = (stop_tilex - start_tilex + 1);
1671     num_tiley = (stop_tiley - start_tiley + 1);
1672     tile_dev = g_new0(gfloat, num_tilex * num_tiley * 2);
1673
1674     ++auto_download_batch_id;
1675
1676     /* Iterate through the tiles and mark which ones need retrieval. */
1677     num_tiles_to_draw = 0;
1678     for(y = 0; y < num_tiley; ++y)
1679     {
1680         for(x = 0; x < num_tilex; ++x)
1681         {
1682             gfloat devx, devy;
1683
1684             /* Find the device location of this tile's center. */
1685             pixel2buf_full(
1686                     tile2pixel(x + start_tilex) + (TILE_SIZE_PIXELS >> 1),
1687                     tile2pixel(y + start_tiley) + (TILE_SIZE_PIXELS >> 1),
1688                     devx, devy,
1689                     mrt->new_center, mrt->zoom, matrix);
1690
1691             /* Apply Map Correction. */
1692             devx -= unit2zpixel(_map_correction_unitx, mrt->zoom);
1693             devy -= unit2zpixel(_map_correction_unity, mrt->zoom);
1694
1695             /* Skip this tile under the following conditions:
1696              * devx < -tile_rothalf_pixels
1697              * devx > _view_width_pixels + tile_rothalf_pixels
1698              * devy < -tile_rothalf_pixels
1699              * devy > _view_height_pixels + tile_rothalf_pixels
1700              */
1701             if(((unsigned)(devx + tile_rothalf_pixels))
1702                     < (_view_width_pixels + (2 * tile_rothalf_pixels))
1703                 && ((unsigned)(devy + tile_rothalf_pixels))
1704                     < (_view_height_pixels + (2 * tile_rothalf_pixels)))
1705             {
1706                 tile_dev[2 * (y * num_tilex + x)] = devx;
1707                 tile_dev[2 * (y * num_tilex + x) + 1] = devy;
1708                 ++num_tiles_to_draw;
1709             }
1710             else
1711             {
1712                 tile_dev[2 * (y * num_tilex + x)] = FLT_MAX;
1713             }
1714         }
1715     }
1716
1717     mrt->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
1718             mrt->screen_width_pixels, mrt->screen_height_pixels);
1719
1720     /* Iterate through the tiles, get them (or queue a download if they're
1721      * not in the cache), and rotate them into the pixbuf. */
1722     for(y = curr_tile_to_draw = 0; y < num_tiley; ++y)
1723     {
1724         gint tiley = y + start_tiley;
1725         for(x = 0; x < num_tilex; ++x)
1726         {
1727             GdkPixbuf *tile_pixbuf = NULL;
1728             gboolean started_download = FALSE;
1729             gint zoff;
1730             gint tilex;
1731
1732             tilex = x + start_tilex;
1733
1734             zoff = mrt->repo->double_size ? 1 : 0;
1735
1736             /* Iteratively try to retrieve a map to draw the tile. */
1737             while((mrt->zoom + zoff) <= MAX_ZOOM && zoff < TILE_SIZE_P2)
1738             {
1739                 /* Check if we're actually going to draw this map. */
1740                 if(tile_dev[2 * (y*num_tilex + x)] != FLT_MAX)
1741                 {
1742                     if(NULL != (tile_pixbuf = mapdb_get(
1743                                     mrt->repo, mrt->zoom + zoff,
1744                                     tilex >> zoff,
1745                                     tiley >> zoff)))
1746                     {
1747                         /* Found a map. */
1748                         break;
1749                     }
1750                 }
1751                 /* Else we're not going to be drawing this map, so just check
1752                  * if it's in the database. */
1753                 else if(mapdb_exists(
1754                                     mrt->repo, mrt->zoom + zoff,
1755                                     tilex >> zoff,
1756                                     tiley >> zoff))
1757                 {
1758                     break;
1759                 }
1760
1761                 /* No map; download, if we should. */
1762                 if(!started_download && _auto_download
1763                         && mrt->repo->type != REPOTYPE_NONE
1764                         /* Make sure this map is within dl zoom limits. */
1765                         && ((unsigned)(mrt->zoom + zoff - mrt->repo->min_zoom)
1766                             <= (mrt->repo->max_zoom - mrt->repo->min_zoom))
1767                         /* Make sure this map matches the dl_zoom_steps,
1768                          * or that there currently is no cache. */
1769                         && (!mrt->repo->db || !((mrt->zoom + zoff
1770                                     - (mrt->repo->double_size ? 1 : 0))
1771                                 % mrt->repo->dl_zoom_steps))
1772                     /* Make sure this tile is even possible. */
1773                     && ((unsigned)(tilex >> zoff)
1774                             < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)
1775                       && (unsigned)(tiley >> zoff)
1776                             < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)))
1777                 {
1778                     started_download = TRUE;
1779
1780                     if(!refresh_latch)
1781                     {
1782                         refresh_latch = g_slice_new(ThreadLatch);
1783                         refresh_latch->is_open = FALSE;
1784                         refresh_latch->is_done_adding_tasks = FALSE;
1785                         refresh_latch->num_tasks = 1;
1786                         refresh_latch->num_done = 0;
1787                         refresh_latch->mutex = g_mutex_new();
1788                         refresh_latch->cond = g_cond_new();
1789                     }
1790                     else
1791                         ++refresh_latch->num_tasks;
1792
1793                     mapdb_initiate_update(
1794                             mrt->repo,
1795                             mrt->zoom + zoff,
1796                             tilex >> zoff,
1797                             tiley >> zoff,
1798                             MAP_UPDATE_AUTO,
1799                             auto_download_batch_id,
1800                             (abs((tilex >> zoff) - unit2ztile(
1801                                      mrt->new_center.unitx, mrt->zoom + zoff))
1802                              + abs((tiley >> zoff) - unit2ztile(
1803                                     mrt->new_center.unity, mrt->zoom + zoff))),
1804                             refresh_latch);
1805                 }
1806
1807                 /* Try again at a coarser resolution. */
1808                 ++zoff;
1809             }
1810
1811             if(tile_pixbuf)
1812             {
1813                 gint boundx, boundy, width, height;
1814                 if(zoff)
1815                     gdk_pixbuf_rotate_matrix_mult_number(matrix, 1 << zoff);
1816                 gdk_pixbuf_rotate(mrt->pixbuf,
1817                         tile_dev[2 * (y * num_tilex + x)],
1818                         tile_dev[2 * (y * num_tilex + x) + 1],
1819                         matrix,
1820                         tile_pixbuf,
1821                         ((tilex - ((tilex >> zoff) << zoff))
1822                             << (TILE_SIZE_P2 - zoff))
1823                             + (TILE_SIZE_PIXELS >> (1 + zoff)),
1824                         ((tiley - ((tiley>>zoff) << zoff))
1825                             << (TILE_SIZE_P2 - zoff))
1826                             + (TILE_SIZE_PIXELS >> (1 + zoff)),
1827                         TILE_SIZE_PIXELS >> zoff,
1828                         TILE_SIZE_PIXELS >> zoff,
1829                         &boundx, &boundy, &width, &height);
1830                 g_object_unref(tile_pixbuf);
1831                 /* Un-multiply the matrix that we used for blitting.  Good
1832                  * thing we're multiplying by powers of two, or this wouldn't
1833                  * work consistently... */
1834                 if(zoff)
1835                     gdk_pixbuf_rotate_matrix_mult_number(
1836                             matrix, 1.f / (1 << zoff));
1837             }
1838             /* usleep(10000); DEBUG */
1839         }
1840     }
1841
1842     /* Don't replace the pixbuf unless/until the mouse is released. */
1843     g_mutex_lock(_mouse_mutex);
1844     ++_pending_replaces;
1845     g_idle_add_full(G_PRIORITY_HIGH_IDLE + 20,
1846             (GSourceFunc)map_replace_pixbuf_idle, mrt, NULL);
1847     g_mutex_unlock(_mouse_mutex);
1848
1849     /* Release the view-change lock. */
1850     if(refresh_latch)
1851     {
1852         g_mutex_lock(refresh_latch->mutex);
1853         if(refresh_latch->num_tasks == refresh_latch->num_done)
1854         {
1855             /* Fast little workers, aren't they? */
1856             g_mutex_unlock(refresh_latch->mutex);
1857             g_cond_free(refresh_latch->cond);
1858             g_mutex_free(refresh_latch->mutex);
1859             g_slice_free(ThreadLatch, refresh_latch);
1860         }
1861         else
1862         {
1863             refresh_latch->is_done_adding_tasks = TRUE;
1864             refresh_latch->is_open = TRUE;
1865             g_cond_signal(refresh_latch->cond);
1866             g_mutex_unlock(refresh_latch->mutex);
1867         }
1868     }
1869
1870     g_free(tile_dev);
1871
1872     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1873     return FALSE;
1874 }
1875
1876 gboolean
1877 map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
1878 {
1879     gint old_view_width_pixels, old_view_height_pixels;
1880     GdkPixbuf *old_map_pixbuf;
1881     printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
1882             _map_widget->allocation.width, _map_widget->allocation.height);
1883
1884     if(_map_widget->allocation.width == 1
1885             && _map_widget->allocation.height == 1)
1886         /* Special case - first allocation - not persistent. */
1887         return TRUE;
1888
1889     old_view_width_pixels = _view_width_pixels;
1890     old_view_height_pixels = _view_height_pixels;
1891     _view_width_pixels = _map_widget->allocation.width;
1892     _view_height_pixels = _map_widget->allocation.height;
1893     _view_halfwidth_pixels = _view_width_pixels / 2;
1894     _view_halfheight_pixels = _view_height_pixels / 2;
1895
1896     g_object_unref(_map_pixmap);
1897     _map_pixmap = gdk_pixmap_new(
1898                 _map_widget->window,
1899                 _view_width_pixels, _view_height_pixels,
1900                 -1); /* -1: use bit depth of widget->window. */
1901
1902     old_map_pixbuf = _map_pixbuf;
1903     _map_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
1904             _view_width_pixels, _view_height_pixels);
1905
1906     {
1907         gint oldnew_diffx = (gint)(_view_width_pixels
1908                 - old_view_width_pixels) / 2;
1909         gint oldnew_diffy = (gint)(_view_height_pixels
1910                 - old_view_height_pixels) / 2;
1911         gdk_pixbuf_copy_area(old_map_pixbuf,
1912                 MAX(0, -oldnew_diffx), MAX(0, -oldnew_diffy),
1913                 MIN(_view_width_pixels, old_view_width_pixels),
1914                 MIN(_view_height_pixels, old_view_height_pixels),
1915                 _map_pixbuf,
1916                 MAX(0, oldnew_diffx), MAX(0, oldnew_diffy));
1917     }
1918
1919     g_object_unref(old_map_pixbuf);
1920
1921     /* Set _scale_rect. */
1922     _scale_rect.x = (_view_width_pixels - SCALE_WIDTH) / 2;
1923     _scale_rect.width = SCALE_WIDTH;
1924     pango_layout_set_text(_scale_layout, "0", -1);
1925     pango_layout_get_pixel_size(_scale_layout, NULL, &_scale_rect.height);
1926     _scale_rect.y = _view_height_pixels - _scale_rect.height - 1;
1927
1928     /* Set _zoom rect. */
1929     pango_layout_set_text(_zoom_layout, "00", -1);
1930     pango_layout_get_pixel_size(_zoom_layout, &_zoom_rect.width,
1931             &_zoom_rect.height);
1932     _zoom_rect.width *= 1.25;
1933     pango_layout_set_width(_zoom_layout, _zoom_rect.width);
1934     pango_layout_context_changed(_zoom_layout);
1935     _zoom_rect.x = _scale_rect.x - _zoom_rect.width;
1936     _zoom_rect.y = _view_height_pixels - _zoom_rect.height - 1;
1937
1938     /* Set _comprose_rect. */
1939     _comprose_rect.x = _view_width_pixels - 25 - _comprose_rect.width;
1940     _comprose_rect.y = _view_height_pixels - 25 - _comprose_rect.height;
1941
1942     map_set_mark();
1943     map_force_redraw();
1944
1945     /* Fire the screen_dimensions_changed DBUS signal. */
1946     dbus_ifc_fire_view_dimensions_changed(
1947             _view_width_pixels, _view_height_pixels);
1948
1949     /* If Auto-Center is set to Lead, then recalc center. */
1950     if(_center_mode == CENTER_LEAD)
1951         map_center_unit(map_calc_new_center(_next_zoom));
1952     else
1953         map_center_unit(_next_center);
1954
1955     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1956     return TRUE;
1957 }
1958
1959 gboolean
1960 sat_panel_expose(GtkWidget *widget, GdkEventExpose *event)
1961 {
1962     gchar *tmp = NULL;
1963     gint x, y;
1964     printf("%s()\n", __PRETTY_FUNCTION__);
1965
1966     draw_sat_info(widget,
1967         0, 0,
1968         widget->allocation.width,
1969         widget->allocation.height,
1970         FALSE);
1971
1972     /* Sat View/In Use */
1973     tmp = g_strdup_printf("%d/%d", _gps.satinuse, _gps.satinview);
1974     pango_layout_set_text(_sat_panel_layout, tmp, -1);
1975     pango_layout_set_alignment(_sat_panel_layout, PANGO_ALIGN_LEFT);
1976     gdk_draw_layout(widget->window,
1977         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1978         20, 2,
1979         _sat_panel_layout);
1980     g_free(tmp);
1981
1982     switch(_gps.fix)
1983     {
1984         case 2:
1985         case 3: tmp = g_strdup_printf("%dD fix", _gps.fix); break;
1986         default: tmp = g_strdup_printf("nofix"); break;
1987     }
1988     pango_layout_set_text(_sat_panel_layout, tmp, -1);
1989     pango_layout_set_alignment(_sat_panel_layout, PANGO_ALIGN_RIGHT);
1990     pango_layout_get_pixel_size(_sat_panel_layout, &x, &y);
1991     gdk_draw_layout(widget->window,
1992         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1993         widget->allocation.width - 20 - x, 2,
1994         _sat_panel_layout);
1995     g_free(tmp);
1996
1997     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1998     return TRUE;
1999 }
2000
2001 gboolean
2002 heading_panel_expose(GtkWidget *widget, GdkEventExpose *event)
2003 {
2004     gint size, xoffset, yoffset, i, x, y;
2005     gint dir;
2006     gfloat tmp;
2007     gchar *text;
2008     printf("%s()\n", __PRETTY_FUNCTION__);
2009
2010     size = MIN(widget->allocation.width, widget->allocation.height);
2011     if(widget->allocation.width > widget->allocation.height)
2012     {
2013         xoffset = (widget->allocation.width - widget->allocation.height) / 2;
2014         yoffset = 0;
2015     }
2016     else
2017     {
2018         xoffset = 0;
2019         yoffset = (widget->allocation.height - widget->allocation.width) / 2;
2020     }
2021     pango_font_description_set_size(_heading_panel_fontdesc,12*PANGO_SCALE);
2022     pango_layout_set_font_description(_heading_panel_layout,
2023             _heading_panel_fontdesc);
2024     pango_layout_set_alignment(_heading_panel_layout, PANGO_ALIGN_CENTER);
2025
2026     text = g_strdup_printf("%3.0f°", _gps.heading);
2027     pango_layout_set_text(_heading_panel_layout, text, -1);
2028     pango_layout_get_pixel_size(_heading_panel_layout, &x, &y);
2029
2030     gdk_draw_layout(widget->window,
2031         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2032         xoffset + size/2 - x/2,
2033         yoffset + size - y - 2, _heading_panel_layout);
2034     g_free(text);
2035
2036     gdk_draw_arc (widget->window,
2037         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2038         FALSE,
2039         xoffset, yoffset + size/2,
2040         size, size,
2041         0, 64 * 180);
2042
2043     /* Simple arrow for heading*/
2044     gdk_draw_line(widget->window,
2045         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2046         xoffset + size/2 + 3,
2047         yoffset + size - y - 5,
2048         xoffset + size/2,
2049         yoffset + size/2 + 5);
2050
2051     gdk_draw_line(widget->window,
2052         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2053         xoffset + size/2 - 3,
2054         yoffset + size - y - 5,
2055         xoffset + size/2,
2056         yoffset + size/2 + 5);
2057
2058     gdk_draw_line(widget->window,
2059         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2060         xoffset + size/2 - 3,
2061         yoffset + size - y - 5,
2062         xoffset + size/2,
2063         yoffset + size - y - 8);
2064
2065     gdk_draw_line(widget->window,
2066         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2067         xoffset + size/2 + 3,
2068         yoffset + size - y - 5,
2069         xoffset + size/2,
2070         yoffset + size - y - 8);
2071
2072     gint angle[5] = {-90,-45,0,45,90};
2073     gint fsize[5] = {0,4,10,4,0};
2074     for(i = 0; i < 5; i++)
2075     {
2076         dir = (gint)(_gps.heading/45)*45 + angle[i];
2077
2078         switch(dir)
2079         {
2080             case   0:
2081             case 360: text = g_strdup("N"); break;
2082             case  45:
2083             case 405:
2084                 text = g_strdup("NE"); break;
2085             case  90:
2086                 text = g_strdup("E"); break;
2087             case 135:
2088                 text = g_strdup("SE"); break;
2089             case 180:
2090                 text = g_strdup("S"); break;
2091             case 225:
2092                 text = g_strdup("SW"); break;
2093             case 270:
2094             case -90:
2095                 text = g_strdup("W"); break;
2096             case 315:
2097             case -45:
2098                 text = g_strdup("NW"); break;
2099             default :
2100                 text = g_strdup("??");
2101                 break;
2102         }
2103
2104         tmp = deg2rad(dir - _gps.heading);
2105         gdk_draw_line(widget->window,
2106             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2107             xoffset + size/2 + ((size/2 - 5) * sinf(tmp)),
2108             yoffset + size - ((size/2 - 5) * cosf(tmp)),
2109             xoffset + size/2 + ((size/2 + 5) * sinf(tmp)),
2110             yoffset + size - ((size/2 + 5) * cosf(tmp)));
2111
2112         x = fsize[i];
2113         if(abs((gint)(_gps.heading/45)*45 - _gps.heading)
2114                 > abs((gint)(_gps.heading/45)*45 + 45 - _gps.heading)
2115                 && (i > 0))
2116             x = fsize[i - 1];
2117
2118         pango_font_description_set_size(_heading_panel_fontdesc,
2119                 (10 + x)*PANGO_SCALE);
2120         pango_layout_set_font_description(_heading_panel_layout,
2121                 _heading_panel_fontdesc);
2122         pango_layout_set_text(_heading_panel_layout, text, -1);
2123         pango_layout_get_pixel_size(_heading_panel_layout, &x, &y);
2124         x = xoffset + size/2 + ((size/2 + 15) * sinf(tmp)) - x/2,
2125         y = yoffset + size - ((size/2 + 15) * cosf(tmp)) - y/2,
2126
2127         gdk_draw_layout(widget->window,
2128             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2129             x, y, _heading_panel_layout);
2130         g_free(text);
2131     }
2132
2133     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2134     return TRUE;
2135 }
2136
2137 gboolean
2138 map_cb_expose(GtkWidget *widget, GdkEventExpose *event)
2139 {
2140     gint i;
2141     printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
2142             event->area.x, event->area.y,
2143             event->area.width, event->area.height);
2144
2145     gdk_draw_drawable(
2146             _map_widget->window,
2147             _gc[COLORABLE_MARK],
2148             _map_pixmap,
2149             event->area.x - _map_offset_devx, event->area.y - _map_offset_devy,
2150             event->area.x, event->area.y,
2151             event->area.width, event->area.height);
2152
2153     /* Draw the mark. */
2154     if((((unsigned)(_mark_bufx1 + _draw_width)
2155                 <= _view_width_pixels+2*_draw_width)
2156              &&((unsigned)(_mark_bufy1 + _draw_width)
2157                  <= _view_height_pixels+2*_draw_width))
2158         || (((unsigned)(_mark_bufx2 + _draw_width)
2159                  <= _view_width_pixels+2*_draw_width)
2160              &&((unsigned)(_mark_bufy2 + _draw_width)
2161                  <= _view_height_pixels+2*_draw_width)))
2162     {
2163         gdk_draw_arc(
2164                 _map_widget->window,
2165                 _gps_state == RCVR_FIXED
2166                     ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD],
2167                 FALSE, /* not filled. */
2168                 _mark_bufx1 - _draw_width + _map_offset_devx,
2169                 _mark_bufy1 - _draw_width + _map_offset_devy,
2170                 2 * _draw_width, 2 * _draw_width,
2171                 0, 360 * 64);
2172         gdk_draw_line(
2173                 _map_widget->window,
2174                 _gps_state == RCVR_FIXED
2175                     ? (_show_velvec
2176                         ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK])
2177                     : _gc[COLORABLE_MARK_OLD],
2178                 _mark_bufx1 + _map_offset_devx,
2179                 _mark_bufy1 + _map_offset_devy,
2180                 _mark_bufx2 + _map_offset_devx,
2181                 _mark_bufy2 + _map_offset_devy);
2182     }
2183
2184     /* draw zoom box if so wanted */
2185     if(_show_zoomlevel) {
2186         gchar *buffer = g_strdup_printf("%d", _zoom);
2187         gdk_draw_rectangle(_map_widget->window,
2188                 _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2189                 TRUE,
2190                 _zoom_rect.x, _zoom_rect.y,
2191                 _zoom_rect.width, _zoom_rect.height);
2192         gdk_draw_rectangle(_map_widget->window,
2193                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2194                 FALSE,
2195                 _zoom_rect.x, _zoom_rect.y,
2196                 _zoom_rect.width, _zoom_rect.height);
2197         pango_layout_set_text(_zoom_layout, buffer, -1);
2198         gdk_draw_layout(_map_widget->window,
2199                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2200                 _zoom_rect.x + _zoom_rect.width / 2,
2201                 _zoom_rect.y, _zoom_layout);
2202     }
2203
2204     /* Draw scale, if necessary. */
2205     if(_show_scale)
2206     {
2207         gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
2208         if(event->area.width && event->area.height)
2209         {
2210             gdk_draw_rectangle(_map_widget->window,
2211                     _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2212                     TRUE,
2213                     _scale_rect.x, _scale_rect.y,
2214                     _scale_rect.width, _scale_rect.height);
2215             gdk_draw_rectangle(_map_widget->window,
2216                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2217                     FALSE,
2218                     _scale_rect.x, _scale_rect.y,
2219                     _scale_rect.width, _scale_rect.height);
2220
2221             /* Now calculate and draw the distance. */
2222             {
2223                 gchar buffer[16];
2224                 gfloat distance;
2225                 gdouble lat1, lon1, lat2, lon2;
2226                 gint width;
2227
2228                 unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4),
2229                         _center.unity, lat1, lon1);
2230                 unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4),
2231                         _center.unity, lat2, lon2);
2232                 distance = calculate_distance(lat1, lon1, lat2, lon2)
2233                     * UNITS_CONVERT[_units];
2234
2235                 if(distance < 1.f)
2236                     snprintf(buffer, sizeof(buffer), "%0.2f %s", distance,
2237                             UNITS_ENUM_TEXT[_units]);
2238                 else if(distance < 10.f)
2239                     snprintf(buffer, sizeof(buffer), "%0.1f %s", distance,
2240                             UNITS_ENUM_TEXT[_units]);
2241                 else
2242                     snprintf(buffer, sizeof(buffer), "%0.f %s", distance,
2243                             UNITS_ENUM_TEXT[_units]);
2244                 pango_layout_set_text(_scale_layout, buffer, -1);
2245
2246                 pango_layout_get_pixel_size(_scale_layout, &width, NULL);
2247
2248                 /* Draw the layout itself. */
2249                 gdk_draw_layout(_map_widget->window,
2250                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2251                     _scale_rect.x + (_scale_rect.width - width) / 2,
2252                     _scale_rect.y, _scale_layout);
2253
2254                 /* Draw little hashes on the ends. */
2255                 gdk_draw_line(_map_widget->window,
2256                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2257                     _scale_rect.x + 4,
2258                     _scale_rect.y + _scale_rect.height / 2 - 4,
2259                     _scale_rect.x + 4,
2260                     _scale_rect.y + _scale_rect.height / 2 + 4);
2261                 gdk_draw_line(_map_widget->window,
2262                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2263                     _scale_rect.x + 4,
2264                     _scale_rect.y + _scale_rect.height / 2,
2265                     _scale_rect.x + (_scale_rect.width - width) / 2 - 4,
2266                     _scale_rect.y + _scale_rect.height / 2);
2267                 gdk_draw_line(_map_widget->window,
2268                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2269                     _scale_rect.x + _scale_rect.width - 4,
2270                     _scale_rect.y + _scale_rect.height / 2 - 4,
2271                     _scale_rect.x + _scale_rect.width - 4,
2272                     _scale_rect.y + _scale_rect.height / 2 + 4);
2273                 gdk_draw_line(_map_widget->window,
2274                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2275                     _scale_rect.x + _scale_rect.width - 4,
2276                     _scale_rect.y + _scale_rect.height / 2,
2277                     _scale_rect.x + (_scale_rect.width + width) / 2 + 4,
2278                     _scale_rect.y + _scale_rect.height / 2);
2279             }
2280         }
2281     }
2282
2283     /* Draw the compass rose, if necessary. */
2284     if(_show_comprose)
2285     {
2286         GdkPoint points[3];
2287         gint offsetx, offsety;
2288         gfloat x, y;
2289
2290         offsetx = _comprose_rect.x;
2291         offsety = _comprose_rect.y;
2292
2293         gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 0, 12);
2294         points[0].x = offsetx + x + 0.5f; points[0].y = offsety + y + 0.5f;
2295         gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 20, 30);
2296         points[1].x = offsetx + x + 0.5f; points[1].y = offsety + y + 0.5f;
2297         gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 0, -45);
2298         points[2].x = offsetx + x + 0.5f; points[2].y = offsety + y + 0.5f;
2299         gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, -20, 30);
2300         points[3].x = offsetx + x + 0.5f; points[3].y = offsety + y + 0.5f;
2301
2302         gdk_draw_polygon(_map_widget->window,
2303                 _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2304                 TRUE, /* FILLED */
2305                 points, 4);
2306         gdk_draw_polygon(_map_widget->window,
2307                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2308                 FALSE, /* NOT FILLED */
2309                 points, 4);
2310
2311         gdk_draw_layout(_map_widget->window,
2312                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2313                 _comprose_rect.x - _comprose_rect.width / 2 + 1,
2314                 _comprose_rect.y - _comprose_rect.height / 2 - 4,
2315                 _comprose_layout);
2316     }
2317
2318     /* Draw a stopwatch if we're redrawing the map. */
2319     for(i = _redraw_count - 1; i >= 0; i--)
2320     {
2321         gdk_draw_pixbuf(
2322                 _map_widget->window,
2323                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2324                 _redraw_wait_icon,
2325                 0, 0,
2326                 _redraw_wait_bounds.x + i
2327                         * HOURGLASS_SEPARATION,
2328                 _redraw_wait_bounds.y,
2329                 _redraw_wait_bounds.width,
2330                 _redraw_wait_bounds.height,
2331                 GDK_RGB_DITHER_NONE, 0, 0);
2332     }
2333
2334     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2335     return TRUE;
2336 }
2337
2338 static void
2339 latlon_cb_copy_clicked(GtkWidget *widget, LatlonDialog *lld) {
2340     gchar buffer[42];
2341
2342     snprintf(buffer, sizeof(buffer),
2343             "%s %s",
2344             gtk_label_get_text(GTK_LABEL(lld->lat)),
2345             gtk_label_get_text(GTK_LABEL(lld->lon)));
2346
2347     gtk_clipboard_set_text(
2348             gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), buffer, -1);
2349 }
2350
2351 static void
2352 latlon_cb_fmt_changed(GtkWidget *widget, LatlonDialog *lld) {
2353   DegFormat fmt;
2354
2355   fmt = gtk_combo_box_get_active(GTK_COMBO_BOX(lld->fmt_combo));
2356
2357   {
2358     gint old = _degformat; /* augh... */
2359     gchar buffer[LL_FMT_LEN];
2360
2361     _degformat = fmt;
2362     lat_format(lld->glat, buffer);
2363     gtk_label_set_label(GTK_LABEL(lld->lat), buffer);
2364     lon_format(lld->glon, buffer);
2365     gtk_label_set_label(GTK_LABEL(lld->lon), buffer);
2366     _degformat = old;
2367   }
2368 }
2369
2370 gboolean
2371 latlon_dialog(gdouble lat, gdouble lon)
2372 {
2373     LatlonDialog lld;
2374     GtkWidget *dialog;
2375     GtkWidget *table;
2376     GtkWidget *label;
2377     GtkWidget *txt_lat;
2378     GtkWidget *txt_lon;
2379     GtkWidget *cmb_format;
2380     GtkWidget *btn_copy = NULL;
2381     printf("%s()\n", __PRETTY_FUNCTION__);
2382
2383     dialog = gtk_dialog_new_with_buttons(_("Show Position"),
2384             GTK_WINDOW(_window), GTK_DIALOG_MODAL,
2385             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2386             NULL);
2387
2388     /* Set the lat/lon strings. */
2389     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2390             table = gtk_table_new(5, 2, FALSE), TRUE, TRUE, 0);
2391
2392     gtk_table_attach(GTK_TABLE(table),
2393             label = gtk_label_new(_("Lat")),
2394             0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2395     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2396     gtk_table_attach(GTK_TABLE(table),
2397             txt_lat = gtk_label_new(""),
2398             1, 2, 0, 1, GTK_FILL, 0, 2, 4);
2399     gtk_misc_set_alignment(GTK_MISC(txt_lat), 1.f, 0.5f);
2400
2401     gtk_table_attach(GTK_TABLE(table),
2402             label = gtk_label_new(_("Lon")),
2403             0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2404     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2405     gtk_table_attach(GTK_TABLE(table),
2406             txt_lon = gtk_label_new(""),
2407             1, 2, 1, 2, GTK_FILL, 0, 2, 4);
2408     gtk_misc_set_alignment(GTK_MISC(txt_lon), 1.f, 0.5f);
2409
2410     gtk_table_attach(GTK_TABLE(table),
2411             label = gtk_label_new(_("Format")),
2412             0, 1, 2, 3, GTK_FILL, 0, 2, 4);
2413     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2414     gtk_table_attach(GTK_TABLE(table),
2415             label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f),
2416             1, 2, 2, 3, GTK_FILL, 0, 2, 4);
2417     gtk_container_add(GTK_CONTAINER(label),
2418             cmb_format = gtk_combo_box_new_text());
2419     gtk_table_attach(GTK_TABLE(table),
2420             btn_copy = gtk_button_new_with_label(_("Copy")),
2421             0, 2, 3, 4, GTK_FILL, 0, 2, 4);
2422
2423     /* Lat/Lon */
2424     {
2425       gchar buffer[LL_FMT_LEN];
2426
2427       lat_format(lat, buffer);
2428       gtk_label_set_label(GTK_LABEL(txt_lat), buffer);
2429       lat_format(lon, buffer);
2430       gtk_label_set_label(GTK_LABEL(txt_lon), buffer);
2431     }
2432
2433     /* Fill in formats */
2434     {
2435       int i;
2436
2437       for(i = 0; i < DEG_FORMAT_ENUM_COUNT; i++) {
2438           gtk_combo_box_append_text(GTK_COMBO_BOX(cmb_format),
2439                   DEG_FORMAT_ENUM_TEXT[i]);
2440       }
2441       gtk_combo_box_set_active(GTK_COMBO_BOX(cmb_format), _degformat);
2442     }
2443
2444
2445     /* setup cb context */
2446     lld.fmt_combo = cmb_format;
2447     lld.glat = lat;
2448     lld.glon = lon;
2449     lld.lat = txt_lat;
2450     lld.lon = txt_lon;
2451
2452     /* Connect Signals */
2453     g_signal_connect(G_OBJECT(cmb_format), "changed",
2454                     G_CALLBACK(latlon_cb_fmt_changed), &lld);
2455     g_signal_connect(G_OBJECT(btn_copy), "clicked",
2456                     G_CALLBACK(latlon_cb_copy_clicked), &lld);
2457
2458     gtk_widget_show_all(dialog);
2459
2460     gtk_dialog_run(GTK_DIALOG(dialog));
2461     gtk_widget_hide(dialog);
2462
2463     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2464     return TRUE;
2465 }
2466
2467
2468 /**
2469  * This is a multi-purpose function for allowing the user to select a file
2470  * for either reading or writing.  If chooser_action is
2471  * GTK_FILE_CHOOSER_ACTION_OPEN, then bytes_out and size_out must be
2472  * non-NULL.  If chooser_action is GTK_FILE_CHOOSER_ACTION_SAVE, then
2473  * handle_out must be non-NULL.  Either dir or file (or both) can be NULL.
2474  * This function returns TRUE if a file was successfully opened.
2475  */
2476 gboolean
2477 display_open_file(GtkWidget *parent, gchar **bytes_out,
2478         GnomeVFSHandle **handle_out, gint *size_out,
2479         gchar **dir, gchar **file, GtkFileChooserAction chooser_action)
2480 {
2481     GtkWidget *dialog;
2482     gboolean success = FALSE;
2483     printf("%s()\n", __PRETTY_FUNCTION__);
2484
2485     dialog = hildon_file_chooser_dialog_new(parent, chooser_action);
2486
2487     if(dir && *dir)
2488         gtk_file_chooser_set_current_folder_uri(
2489                 GTK_FILE_CHOOSER(dialog), *dir);
2490     if(file && *file)
2491     {
2492         gtk_file_chooser_set_uri(
2493                 GTK_FILE_CHOOSER(dialog), *file);
2494         if(chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE)
2495         {
2496             /* Work around a bug in HildonFileChooserDialog. */
2497             gchar *basename = g_path_get_basename(*file);
2498             g_object_set(G_OBJECT(dialog), "autonaming", FALSE, NULL);
2499             gtk_file_chooser_set_current_name(
2500                     GTK_FILE_CHOOSER(dialog), basename);
2501             g_free(basename);
2502         }
2503     }
2504
2505     gtk_widget_show_all(dialog);
2506
2507     while(!success && gtk_dialog_run(GTK_DIALOG(dialog))==GTK_RESPONSE_OK)
2508     {
2509         gchar *file_uri_str;
2510         GnomeVFSResult vfs_result;
2511
2512         /* Get the selected filename. */
2513         file_uri_str = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
2514
2515         if((chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN
2516                 && (GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
2517                         file_uri_str, size_out, bytes_out))))
2518                 || (chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE
2519                     && GNOME_VFS_OK != (vfs_result = gnome_vfs_create(
2520                             handle_out, file_uri_str,
2521                             GNOME_VFS_OPEN_WRITE, FALSE, 0664))))
2522         {
2523             gchar buffer[BUFFER_SIZE];
2524             snprintf(buffer, sizeof(buffer),
2525                     "%s:\n%s", chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN
2526                                 ? _("Failed to open file for reading")
2527                                 : _("Failed to open file for writing"),
2528                     gnome_vfs_result_to_string(vfs_result));
2529             popup_error(dialog, buffer);
2530         }
2531         else
2532             success = TRUE;
2533
2534         g_free(file_uri_str);
2535     }
2536
2537     if(success)
2538     {
2539         /* Success!. */
2540         if(dir)
2541         {
2542             g_free(*dir);
2543             *dir = gtk_file_chooser_get_current_folder_uri(
2544                     GTK_FILE_CHOOSER(dialog));
2545         }
2546
2547         /* If desired, save the file for later. */
2548         if(file)
2549         {
2550             g_free(*file);
2551             *file = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
2552         }
2553     }
2554
2555     gtk_widget_destroy(dialog);
2556
2557     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, success);
2558     return success;
2559 }
2560
2561 void
2562 display_init()
2563 {
2564     PangoContext *pango_context;
2565     PangoFontDescription *pango_font;
2566     GdkColor color;
2567     printf("%s()\n", __PRETTY_FUNCTION__);
2568
2569     /* Cache some pango and GCs for drawing. */
2570     pango_context = gtk_widget_get_pango_context(_map_widget);
2571     _scale_layout = pango_layout_new(pango_context);
2572     pango_font = pango_font_description_new();
2573     pango_font_description_set_size(pango_font, 12 * PANGO_SCALE);
2574     pango_layout_set_font_description(_scale_layout, pango_font);
2575
2576     /* zoom box */
2577     pango_context = gtk_widget_get_pango_context(_map_widget);
2578     _zoom_layout = pango_layout_new(pango_context);
2579     pango_font = pango_font_description_new();
2580     pango_font_description_set_size(pango_font, 12 * PANGO_SCALE);
2581     pango_layout_set_font_description(_zoom_layout, pango_font);
2582     pango_layout_set_alignment(_zoom_layout, PANGO_ALIGN_CENTER);
2583
2584     /* compose rose */
2585     pango_context = gtk_widget_get_pango_context(_map_widget);
2586     _comprose_layout = pango_layout_new(pango_context);
2587     pango_font = pango_font_description_new();
2588     pango_font_description_set_size(pango_font, 16 * PANGO_SCALE);
2589     pango_font_description_set_weight(pango_font, PANGO_WEIGHT_BOLD);
2590     pango_layout_set_font_description(_comprose_layout, pango_font);
2591     pango_layout_set_alignment(_comprose_layout, PANGO_ALIGN_CENTER);
2592     pango_layout_set_text(_comprose_layout, "N", -1);
2593     {
2594         PangoRectangle rect;
2595         pango_layout_get_pixel_extents(_comprose_layout, &rect, NULL);
2596         _comprose_rect.width = rect.width + 3;
2597         _comprose_rect.height = rect.height + 3;
2598     }
2599
2600     /* speed limit */
2601     _speed_limit_gc1 = gdk_gc_new (_map_widget->window);
2602     color.red = 0xffff;
2603     color.green = 0;
2604     color.blue = 0;
2605     gdk_gc_set_rgb_fg_color(_speed_limit_gc1, &color);
2606     color.red = 0;
2607     color.green = 0;
2608     color.blue = 0;
2609     _speed_limit_gc2 = gdk_gc_new(_map_widget->window);
2610     gdk_gc_set_rgb_fg_color(_speed_limit_gc2, &color);
2611     pango_context = gtk_widget_get_pango_context(_map_widget);
2612     _speed_limit_layout = pango_layout_new(pango_context);
2613     pango_font = pango_font_description_new();
2614     pango_font_description_set_size(pango_font, 64 * PANGO_SCALE);
2615     pango_layout_set_font_description(_speed_limit_layout,
2616             pango_font);
2617     pango_layout_set_alignment(_speed_limit_layout, PANGO_ALIGN_CENTER);
2618
2619     /* draw_sat_info() */
2620     _sat_info_gc1 = gdk_gc_new(_map_widget->window);
2621     color.red = 0;
2622     color.green = 0;
2623     color.blue = 0;
2624     gdk_gc_set_rgb_fg_color(_sat_info_gc1, &color);
2625     color.red = 0;
2626     color.green = 0;
2627     color.blue = 0xffff;
2628     _sat_info_gc2 = gdk_gc_new(_map_widget->window);
2629     gdk_gc_set_rgb_fg_color(_sat_info_gc2, &color);
2630     pango_context = gtk_widget_get_pango_context(_map_widget);
2631     _sat_info_layout = pango_layout_new(pango_context);
2632     pango_font = pango_font_description_new();
2633     pango_font_description_set_family(pango_font,"Sans Serif");
2634     pango_font_description_set_size(pango_font, 8*PANGO_SCALE);
2635     pango_layout_set_font_description(_sat_info_layout, pango_font);
2636     pango_layout_set_alignment(_sat_info_layout, PANGO_ALIGN_CENTER);
2637
2638     /* sat_panel_expose() */
2639     pango_context = gtk_widget_get_pango_context(_map_widget);
2640     _sat_panel_layout = pango_layout_new(pango_context);
2641     pango_font = pango_font_description_new();
2642     pango_font_description_set_family(pango_font,"Sans Serif");
2643     pango_font_description_set_size(pango_font, 14*PANGO_SCALE);
2644     pango_layout_set_font_description (_sat_panel_layout, pango_font);
2645
2646     /* heading_panel_expose() */
2647     pango_context = gtk_widget_get_pango_context(_map_widget);
2648     _heading_panel_layout = pango_layout_new(pango_context);
2649     _heading_panel_fontdesc =  pango_font_description_new();
2650     pango_font_description_set_family(_heading_panel_fontdesc, "Sans Serif");
2651
2652     /* draw_sat_details() */
2653     pango_context = gtk_widget_get_pango_context(_map_widget);
2654     _sat_details_layout = pango_layout_new(pango_context);
2655     pango_font = pango_font_description_new();
2656     pango_font_description_set_family(pango_font,"Sans Serif");
2657     pango_font_description_set_size(pango_font, 10*PANGO_SCALE);
2658     pango_layout_set_font_description(_sat_details_layout,
2659             pango_font);
2660     pango_layout_set_alignment(_sat_details_layout, PANGO_ALIGN_CENTER);
2661
2662     /* sat_details_panel_expose() */
2663     pango_context = gtk_widget_get_pango_context(_map_widget);
2664     _sat_details_expose_layout = pango_layout_new(pango_context);
2665     pango_font = pango_font_description_new();
2666     pango_font_description_set_family(
2667             pango_font,"Sans Serif");
2668     pango_layout_set_alignment(_sat_details_expose_layout,
2669             PANGO_ALIGN_CENTER);
2670     pango_font_description_set_size(pango_font,
2671             14*PANGO_SCALE);
2672     pango_layout_set_font_description(_sat_details_expose_layout,
2673             pango_font);
2674
2675     /* Load the _redraw_wait_icon. */
2676     {
2677         GError *error = NULL;
2678         gchar *icon_path = "/usr/share/icons/hicolor/scalable/hildon"
2679                            "/maemo-mapper-wait.png";
2680         _redraw_wait_bounds.x = 0;
2681         _redraw_wait_bounds.y = 0;
2682         _redraw_wait_icon = gdk_pixbuf_new_from_file(icon_path, &error);
2683         if(!_redraw_wait_icon || error)
2684         {
2685             g_printerr("Error parsing pixbuf: %s\n",
2686                     error ? error->message : icon_path);
2687             _redraw_wait_bounds.width = 0;
2688             _redraw_wait_bounds.height = 0;
2689             _redraw_wait_icon = NULL;
2690         }
2691         else
2692         {
2693             _redraw_wait_bounds.width
2694                 = gdk_pixbuf_get_width(_redraw_wait_icon);
2695             _redraw_wait_bounds.height
2696                 = gdk_pixbuf_get_height(_redraw_wait_icon);
2697         }
2698     }
2699
2700     g_signal_connect(G_OBJECT(_map_widget), "configure_event",
2701             G_CALLBACK(map_cb_configure), NULL);
2702
2703     g_signal_connect(G_OBJECT(_map_widget), "expose_event",
2704             G_CALLBACK(map_cb_expose), NULL);
2705     g_signal_connect(G_OBJECT(_sat_panel), "expose_event",
2706             G_CALLBACK(sat_panel_expose), NULL);
2707     g_signal_connect(G_OBJECT(_heading_panel), "expose_event",
2708             G_CALLBACK(heading_panel_expose), NULL);
2709
2710     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2711 }