]> git.itanic.dy.fi Git - maemo-mapper/blob - src/display.c
381b92339912b6eda9b3679ab2916ad03f975ab9
[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)
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             if (mut->repo->layer_level == 0) {
1500                 gchar buffer[BUFFER_SIZE];
1501                 snprintf(buffer, sizeof(buffer), "%d %s", _dl_errors,
1502                          _("maps failed to download."));
1503                 MACRO_BANNER_SHOW_INFO(_window, buffer);
1504             }
1505             _dl_errors = 0;
1506         }
1507
1508         if(mut->update_type != MAP_UPDATE_AUTO || _refresh_map_after_download)
1509         {
1510             /* Update the map. */
1511             map_refresh_mark(TRUE);
1512         }
1513     }
1514     else if(_download_banner)
1515     {
1516         hildon_banner_set_fraction(HILDON_BANNER(_download_banner),
1517                 _curr_download / (double)_num_downloads);
1518     }
1519
1520     g_mutex_lock(_mut_priority_mutex);
1521     g_hash_table_remove(_mut_exists_table, mut);
1522     g_mutex_unlock(_mut_priority_mutex);
1523
1524     g_slice_free(MapUpdateTask, mut);
1525
1526     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1527     return FALSE;
1528 }
1529
1530 /**
1531  * Set the current zoom level.  If the given zoom level is the same as the
1532  * current zoom level, or if the new zoom is invalid
1533  * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
1534  */
1535 void
1536 map_set_zoom(gint new_zoom)
1537 {
1538     printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom);
1539
1540     /* This if condition also checks for new_zoom >= 0. */
1541     if((unsigned)new_zoom > MAX_ZOOM)
1542         return;
1543
1544     map_center_zoom(new_zoom / _curr_repo->view_zoom_steps
1545                      * _curr_repo->view_zoom_steps);
1546
1547     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1548 }
1549
1550 static gboolean
1551 map_replace_pixbuf_idle(MapRenderTask *mrt)
1552 {
1553     printf("%s()\n", __PRETTY_FUNCTION__);
1554
1555     if(!--_pending_replaces && !_mouse_is_down
1556             && mrt->screen_width_pixels == _view_width_pixels
1557             && mrt->screen_height_pixels == _view_height_pixels)
1558     {
1559         g_object_unref(_map_pixbuf);
1560         _map_pixbuf = mrt->pixbuf;
1561
1562         if(_center.unitx != mrt->new_center.unitx
1563                 || _center.unity != mrt->new_center.unity
1564                 || _zoom != mrt->zoom
1565                 || _map_rotate_angle != mrt->rotate_angle)
1566         {
1567             dbus_ifc_fire_view_position_changed(
1568                     mrt->new_center, mrt->zoom, mrt->rotate_angle);
1569         }
1570
1571         _center = mrt->new_center;
1572         _zoom = mrt->zoom;
1573         _map_rotate_angle = mrt->rotate_angle;
1574
1575         gdk_pixbuf_rotate_matrix_fill_for_rotation(
1576                 _map_rotate_matrix,
1577                 deg2rad(ROTATE_DIR_ENUM_DEGREES[_rotate_dir]
1578                     - _map_rotate_angle));
1579         gdk_pixbuf_rotate_matrix_fill_for_rotation(
1580                 _map_reverse_matrix,
1581                 deg2rad(_map_rotate_angle
1582                     - ROTATE_DIR_ENUM_DEGREES[_rotate_dir]));
1583
1584         --_redraw_count;
1585
1586         _map_offset_devx = 0;
1587         _map_offset_devy = 0;
1588
1589         map_set_mark();
1590         map_force_redraw();
1591     }
1592     else
1593     {
1594         /* Ignore this new pixbuf. We have newer ones coming. */
1595         g_object_unref(mrt->pixbuf);
1596         --_redraw_count;
1597     }
1598
1599     g_slice_free(MapRenderTask, mrt);
1600
1601     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1602     return FALSE;
1603 }
1604
1605
1606 /* Routine draws one partly-transparent pixbuf on top of the another (base map). For efficiency, we
1607    assume that base map's tile have no transparent pixels (because it should not have them). We also
1608    assume that pixbufs are have the same size. */
1609 static void
1610 combine_tiles (GdkPixbuf *dst_pixbuf, GdkPixbuf *src_pixbuf)
1611 {
1612     gint s_n_channels = gdk_pixbuf_get_n_channels (src_pixbuf);
1613     gint d_n_channels = gdk_pixbuf_get_n_channels (dst_pixbuf);
1614     gint bps = gdk_pixbuf_get_bits_per_sample (dst_pixbuf);
1615     gint width, height, x, y, d_delta, s_delta;
1616     guchar *d_p, *s_p;
1617
1618     if (gdk_pixbuf_get_colorspace (dst_pixbuf) != gdk_pixbuf_get_colorspace (src_pixbuf)) {
1619         printf ("combine return (1)\n");
1620         return;
1621     }
1622     if (gdk_pixbuf_get_colorspace (dst_pixbuf) != GDK_COLORSPACE_RGB) {
1623         printf ("combine return (2)\n");
1624         return;
1625     }
1626
1627     if (bps != gdk_pixbuf_get_bits_per_sample (src_pixbuf)) {
1628         printf ("combine return (5)\n");
1629         return;
1630     }
1631
1632     width = gdk_pixbuf_get_width (dst_pixbuf);
1633     height = gdk_pixbuf_get_height (dst_pixbuf);
1634
1635     if (width != gdk_pixbuf_get_width (src_pixbuf)) {
1636         printf ("combine return (6)\n");
1637         return;
1638     }
1639     if (height != gdk_pixbuf_get_height (src_pixbuf)) {
1640         printf ("combine return (7)\n");
1641         return;
1642     }
1643
1644     s_delta = (bps >> 3) * s_n_channels;
1645     d_delta = (bps >> 3) * d_n_channels;
1646     d_p = gdk_pixbuf_get_pixels (dst_pixbuf);
1647     s_p = gdk_pixbuf_get_pixels (src_pixbuf);
1648
1649     /* ok, we're ready to combine */
1650     for (y = 0; y < height; y++) {
1651         for (x = 0; x < width; x++, d_p += d_delta, s_p += s_delta) {
1652             /* TODO: alpha blending? */
1653             if (s_n_channels == 3 || s_p[3]) {
1654                 d_p[0] = s_p[0];
1655                 d_p[1] = s_p[1];
1656                 d_p[2] = s_p[2];
1657             }
1658         }
1659     }
1660 }
1661
1662
1663 gboolean
1664 thread_render_map(MapRenderTask *mrt)
1665 {
1666     gfloat matrix[4];
1667     gint start_tilex, start_tiley, stop_tilex, stop_tiley;
1668     gint x = 0, y, num_tilex, num_tiley;
1669     gint diag_halflength_units;
1670     gfloat angle_rad;
1671     gint tile_rothalf_pixels;
1672     gint curr_tile_to_draw, num_tiles_to_draw;
1673     gfloat *tile_dev;
1674     ThreadLatch *refresh_latch = NULL;
1675     gint cache_amount;
1676     static gint8 auto_download_batch_id = INT8_MIN;
1677     printf("%s(%d, %d, %d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
1678             mrt->screen_width_pixels, mrt->screen_height_pixels,
1679             mrt->new_center.unitx, mrt->new_center.unity, mrt->zoom,
1680             mrt->rotate_angle);
1681
1682     /* If there are more render tasks in the queue, skip this one. */
1683     if(g_thread_pool_unprocessed(_mrt_thread_pool))
1684     {
1685         g_slice_free(MapRenderTask, mrt);
1686         --_redraw_count;
1687         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1688         return FALSE;
1689     }
1690
1691     angle_rad = deg2rad(ROTATE_DIR_ENUM_DEGREES[_rotate_dir]
1692             - mrt->rotate_angle);
1693
1694     gdk_pixbuf_rotate_matrix_fill_for_rotation(matrix, angle_rad);
1695
1696     /* Determine (roughly) the tiles we might have to process.
1697      * Basically, we take the center unit and subtract the maximum dimension
1698      * of the screen plus the maximum additional pixels of a rotated tile.
1699      */
1700     tile_rothalf_pixels = MAX(
1701             fabsf(TILE_HALFDIAG_PIXELS * sinf((PI / 4) - angle_rad)),
1702             fabsf(TILE_HALFDIAG_PIXELS * cosf((PI / 4) - angle_rad)));
1703
1704     mrt->zoom = _next_zoom;
1705
1706     if(mrt->repo->type != REPOTYPE_NONE && mrt->repo->db)
1707         cache_amount = _auto_download_precache;
1708     else
1709         cache_amount = 1; /* No cache. */
1710
1711     diag_halflength_units = pixel2zunit(TILE_HALFDIAG_PIXELS
1712         + MAX(mrt->screen_width_pixels, mrt->screen_height_pixels) / 2,
1713         mrt->zoom);
1714     start_tilex = unit2ztile(
1715             mrt->new_center.unitx - diag_halflength_units
1716             + _map_correction_unitx, mrt->zoom);
1717     start_tilex = MAX(start_tilex - (cache_amount - 1), 0);
1718     start_tiley = unit2ztile(
1719             mrt->new_center.unity - diag_halflength_units
1720             + _map_correction_unity, mrt->zoom);
1721     start_tiley = MAX(start_tiley - (cache_amount - 1), 0);
1722     stop_tilex = unit2ztile(mrt->new_center.unitx + diag_halflength_units
1723             + _map_correction_unitx, mrt->zoom);
1724     stop_tilex = MIN(stop_tilex + (cache_amount - 1),
1725             unit2ztile(WORLD_SIZE_UNITS, mrt->zoom));
1726     stop_tiley = unit2ztile(mrt->new_center.unity + diag_halflength_units
1727             + _map_correction_unity, mrt->zoom);
1728     stop_tiley = MIN(stop_tiley + (cache_amount - 1),
1729             unit2ztile(WORLD_SIZE_UNITS, mrt->zoom));
1730
1731     num_tilex = (stop_tilex - start_tilex + 1);
1732     num_tiley = (stop_tiley - start_tiley + 1);
1733     tile_dev = g_new0(gfloat, num_tilex * num_tiley * 2);
1734
1735     ++auto_download_batch_id;
1736
1737     /* Iterate through the tiles and mark which ones need retrieval. */
1738     num_tiles_to_draw = 0;
1739     for(y = 0; y < num_tiley; ++y)
1740     {
1741         for(x = 0; x < num_tilex; ++x)
1742         {
1743             gfloat devx, devy;
1744
1745             /* Find the device location of this tile's center. */
1746             pixel2buf_full(
1747                     tile2pixel(x + start_tilex) + (TILE_SIZE_PIXELS >> 1),
1748                     tile2pixel(y + start_tiley) + (TILE_SIZE_PIXELS >> 1),
1749                     devx, devy,
1750                     mrt->new_center, mrt->zoom, matrix);
1751
1752             /* Apply Map Correction. */
1753             devx -= unit2zpixel(_map_correction_unitx, mrt->zoom);
1754             devy -= unit2zpixel(_map_correction_unity, mrt->zoom);
1755
1756             /* Skip this tile under the following conditions:
1757              * devx < -tile_rothalf_pixels
1758              * devx > _view_width_pixels + tile_rothalf_pixels
1759              * devy < -tile_rothalf_pixels
1760              * devy > _view_height_pixels + tile_rothalf_pixels
1761              */
1762             if(((unsigned)(devx + tile_rothalf_pixels))
1763                     < (_view_width_pixels + (2 * tile_rothalf_pixels))
1764                 && ((unsigned)(devy + tile_rothalf_pixels))
1765                     < (_view_height_pixels + (2 * tile_rothalf_pixels)))
1766             {
1767                 tile_dev[2 * (y * num_tilex + x)] = devx;
1768                 tile_dev[2 * (y * num_tilex + x) + 1] = devy;
1769                 ++num_tiles_to_draw;
1770             }
1771             else
1772             {
1773                 tile_dev[2 * (y * num_tilex + x)] = FLT_MAX;
1774             }
1775         }
1776     }
1777
1778     mrt->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
1779             mrt->screen_width_pixels, mrt->screen_height_pixels);
1780     _refresh_map_after_download = FALSE;
1781
1782     /* Iterate through the tiles, get them (or queue a download if they're
1783      * not in the cache), and rotate them into the pixbuf. */
1784     for(y = curr_tile_to_draw = 0; y < num_tiley; ++y)
1785     {
1786         gint tiley = y + start_tiley;
1787         for(x = 0; x < num_tilex; ++x)
1788         {
1789             GdkPixbuf *tile_pixbuf = NULL, *layer_pixbuf = NULL;
1790             gboolean started_download = FALSE;
1791             gint zoff, zoff_base;
1792             gint tilex;
1793             RepoData* repo_p = mrt->repo;
1794
1795             tilex = x + start_tilex;
1796             zoff_base = mrt->repo->double_size ? 1 : 0;
1797
1798             /* iterating over tile and all it's layers */
1799             while (repo_p)
1800             {
1801                 started_download = FALSE;
1802
1803                 /* for layers we must use resolution of underlying map */
1804                 zoff = zoff_base;
1805
1806                 /* if this is not a bottom layer and layer not enabled, skip it */
1807                 if (repo_p != mrt->repo && !repo_p->layer_enabled)
1808                 {
1809                     repo_p = repo_p->layers;
1810                     continue;
1811                 }
1812
1813                 /* Iteratively try to retrieve a map to draw the tile. */
1814                 while((mrt->zoom + zoff) <= MAX_ZOOM && zoff < 4)
1815                 {
1816                     /* Check if we're actually going to draw this map. */
1817                     if(tile_dev[2 * (y*num_tilex + x)] != FLT_MAX)
1818                     {
1819                         if(NULL != (layer_pixbuf = mapdb_get(
1820                                         repo_p, mrt->zoom + zoff,
1821                                         tilex >> zoff,
1822                                         tiley >> zoff)))
1823                         {
1824                             /* Found a map. Check for it's age. */
1825                             gint age = get_tile_age (layer_pixbuf);
1826                             printf ("Tile age (%d)\n", age);
1827
1828                             /* throw away tile only if we can download something */
1829                             if (!repo_p->layer_refresh_interval ||
1830                                 age < repo_p->layer_refresh_interval * 60 ||
1831                                 !_auto_download)
1832                             {
1833                                 /* if this is a layer's tile, join with main tile */
1834                                 if (repo_p != mrt->repo)
1835                                 {
1836                                     /* but only if main layer is exists */
1837                                     if (tile_pixbuf)
1838                                         combine_tiles (tile_pixbuf, layer_pixbuf);
1839                                     g_object_unref (layer_pixbuf);
1840                                 }
1841                                 else {
1842                                     tile_pixbuf = layer_pixbuf;
1843                                     zoff_base = zoff;
1844                                 }
1845                                 break;
1846                             }
1847                             else
1848                                 g_object_unref (layer_pixbuf);
1849                         }
1850                         else
1851                             if (repo_p->layers)
1852                                 _refresh_map_after_download = TRUE;
1853                     }
1854                     /* Else we're not going to be drawing this map, so just check
1855                      * if it's in the database. */
1856                     else if(mapdb_exists(
1857                                         repo_p, mrt->zoom + zoff,
1858                                         tilex >> zoff,
1859                                         tiley >> zoff))
1860                     {
1861                         zoff_base = zoff;
1862                         break;
1863                     }
1864
1865                     /* No map; download, if we should. */
1866                     if(!started_download && _auto_download
1867                        && mrt->repo->type != REPOTYPE_NONE
1868                        /* Make sure this map is within dl zoom limits. */
1869                        && ((unsigned)(mrt->zoom + zoff - mrt->repo->min_zoom)
1870                            <= (mrt->repo->max_zoom - mrt->repo->min_zoom))
1871                        /* Make sure this map matches the dl_zoom_steps,
1872                         * or that there currently is no cache. */
1873                        && (!repo_p->db || !((mrt->zoom + zoff
1874                                              - (mrt->repo->double_size ? 1 : 0))
1875                                             % mrt->repo->dl_zoom_steps))
1876                        /* Make sure this tile is even possible. */
1877                        && ((unsigned)(tilex >> zoff)
1878                            < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)
1879                            && (unsigned)(tiley >> zoff)
1880                            < unit2ztile(WORLD_SIZE_UNITS, mrt->zoom + zoff)))
1881                     {
1882                         started_download = TRUE;
1883
1884                         if(!refresh_latch)
1885                         {
1886                             refresh_latch = g_slice_new(ThreadLatch);
1887                             refresh_latch->is_open = FALSE;
1888                             refresh_latch->is_done_adding_tasks = FALSE;
1889                             refresh_latch->num_tasks = 1;
1890                             refresh_latch->num_done = 0;
1891                             refresh_latch->mutex = g_mutex_new();
1892                             refresh_latch->cond = g_cond_new();
1893                         }
1894                         else
1895                             ++refresh_latch->num_tasks;
1896
1897                         mapdb_initiate_update(
1898                                 repo_p,
1899                                 mrt->zoom + zoff,
1900                                 tilex >> zoff,
1901                                 tiley >> zoff,
1902                                 MAP_UPDATE_AUTO,
1903                                 auto_download_batch_id,
1904                                 (abs((tilex >> zoff) - unit2ztile(
1905                                      mrt->new_center.unitx, mrt->zoom + zoff))
1906                                  + abs((tiley >> zoff) - unit2ztile(
1907                                      mrt->new_center.unity, mrt->zoom + zoff))),
1908                                 refresh_latch);
1909                     }
1910
1911                     /* Try again at a coarser resolution. Only for underlying map.*/
1912                     if (repo_p == mrt->repo && repo_p->type != REPOTYPE_NONE)
1913                         ++zoff;
1914                     else
1915                         break;
1916                 }
1917
1918                 repo_p = repo_p->layers;
1919             }
1920
1921             /* use zoom of the base map */
1922             zoff = zoff_base;
1923
1924             if(tile_pixbuf)
1925             {
1926                 gint boundx, boundy, width, height;
1927
1928                 if(zoff)
1929                     gdk_pixbuf_rotate_matrix_mult_number(matrix, 1 << zoff);
1930                 gdk_pixbuf_rotate(mrt->pixbuf,
1931                         tile_dev[2 * (y * num_tilex + x)],
1932                         tile_dev[2 * (y * num_tilex + x) + 1],
1933                         matrix,
1934                         tile_pixbuf,
1935                         ((tilex - ((tilex >> zoff) << zoff))
1936                             << (TILE_SIZE_P2 - zoff))
1937                             + (TILE_SIZE_PIXELS >> (1 + zoff)),
1938                         ((tiley - ((tiley>>zoff) << zoff))
1939                             << (TILE_SIZE_P2 - zoff))
1940                             + (TILE_SIZE_PIXELS >> (1 + zoff)),
1941                         TILE_SIZE_PIXELS >> zoff,
1942                         TILE_SIZE_PIXELS >> zoff,
1943                         &boundx, &boundy, &width, &height);
1944                 g_object_unref(tile_pixbuf);
1945                 /* Un-multiply the matrix that we used for blitting.  Good
1946                  * thing we're multiplying by powers of two, or this wouldn't
1947                  * work consistently... */
1948                 if(zoff)
1949                     gdk_pixbuf_rotate_matrix_mult_number(
1950                             matrix, 1.f / (1 << zoff));
1951             }
1952             /* usleep(10000); DEBUG */
1953         }
1954     }
1955
1956     /* Don't replace the pixbuf unless/until the mouse is released. */
1957     g_mutex_lock(_mouse_mutex);
1958     ++_pending_replaces;
1959     g_idle_add_full(G_PRIORITY_HIGH_IDLE + 20,
1960             (GSourceFunc)map_replace_pixbuf_idle, mrt, NULL);
1961     g_mutex_unlock(_mouse_mutex);
1962
1963     /* Release the view-change lock. */
1964     if(refresh_latch)
1965     {
1966         g_mutex_lock(refresh_latch->mutex);
1967         if(refresh_latch->num_tasks == refresh_latch->num_done)
1968         {
1969             /* Fast little workers, aren't they? */
1970             g_mutex_unlock(refresh_latch->mutex);
1971             g_cond_free(refresh_latch->cond);
1972             g_mutex_free(refresh_latch->mutex);
1973             g_slice_free(ThreadLatch, refresh_latch);
1974         }
1975         else
1976         {
1977             refresh_latch->is_done_adding_tasks = TRUE;
1978             refresh_latch->is_open = TRUE;
1979             g_cond_signal(refresh_latch->cond);
1980             g_mutex_unlock(refresh_latch->mutex);
1981         }
1982     }
1983
1984     g_free(tile_dev);
1985
1986     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1987     return FALSE;
1988 }
1989
1990 gboolean
1991 map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
1992 {
1993     gint old_view_width_pixels, old_view_height_pixels;
1994     GdkPixbuf *old_map_pixbuf;
1995     printf("%s(%d, %d)\n", __PRETTY_FUNCTION__,
1996             _map_widget->allocation.width, _map_widget->allocation.height);
1997
1998     if(_map_widget->allocation.width == 1
1999             && _map_widget->allocation.height == 1)
2000         /* Special case - first allocation - not persistent. */
2001         return TRUE;
2002
2003     old_view_width_pixels = _view_width_pixels;
2004     old_view_height_pixels = _view_height_pixels;
2005     _view_width_pixels = _map_widget->allocation.width;
2006     _view_height_pixels = _map_widget->allocation.height;
2007     _view_halfwidth_pixels = _view_width_pixels / 2;
2008     _view_halfheight_pixels = _view_height_pixels / 2;
2009
2010     g_object_unref(_map_pixmap);
2011     _map_pixmap = gdk_pixmap_new(
2012                 _map_widget->window,
2013                 _view_width_pixels, _view_height_pixels,
2014                 -1); /* -1: use bit depth of widget->window. */
2015
2016     old_map_pixbuf = _map_pixbuf;
2017     _map_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
2018             _view_width_pixels, _view_height_pixels);
2019
2020     {
2021         gint oldnew_diffx = (gint)(_view_width_pixels
2022                 - old_view_width_pixels) / 2;
2023         gint oldnew_diffy = (gint)(_view_height_pixels
2024                 - old_view_height_pixels) / 2;
2025         gdk_pixbuf_copy_area(old_map_pixbuf,
2026                 MAX(0, -oldnew_diffx), MAX(0, -oldnew_diffy),
2027                 MIN(_view_width_pixels, old_view_width_pixels),
2028                 MIN(_view_height_pixels, old_view_height_pixels),
2029                 _map_pixbuf,
2030                 MAX(0, oldnew_diffx), MAX(0, oldnew_diffy));
2031     }
2032
2033     g_object_unref(old_map_pixbuf);
2034
2035     /* Set _scale_rect. */
2036     _scale_rect.x = (_view_width_pixels - SCALE_WIDTH) / 2;
2037     _scale_rect.width = SCALE_WIDTH;
2038     pango_layout_set_text(_scale_layout, "0", -1);
2039     pango_layout_get_pixel_size(_scale_layout, NULL, &_scale_rect.height);
2040     _scale_rect.y = _view_height_pixels - _scale_rect.height - 1;
2041
2042     /* Set _zoom rect. */
2043     pango_layout_set_text(_zoom_layout, "00", -1);
2044     pango_layout_get_pixel_size(_zoom_layout, &_zoom_rect.width,
2045             &_zoom_rect.height);
2046     _zoom_rect.width *= 1.25;
2047     pango_layout_set_width(_zoom_layout, _zoom_rect.width);
2048     pango_layout_context_changed(_zoom_layout);
2049     _zoom_rect.x = _scale_rect.x - _zoom_rect.width;
2050     _zoom_rect.y = _view_height_pixels - _zoom_rect.height - 1;
2051
2052     /* Set _comprose_rect. */
2053     _comprose_rect.x = _view_width_pixels - 25 - _comprose_rect.width;
2054     _comprose_rect.y = _view_height_pixels - 25 - _comprose_rect.height;
2055
2056     map_set_mark();
2057     map_force_redraw();
2058
2059     /* Fire the screen_dimensions_changed DBUS signal. */
2060     dbus_ifc_fire_view_dimensions_changed(
2061             _view_width_pixels, _view_height_pixels);
2062
2063     /* If Auto-Center is set to Lead, then recalc center. */
2064     if(_center_mode == CENTER_LEAD)
2065         map_center_unit(map_calc_new_center(_next_zoom));
2066     else
2067         map_center_unit(_next_center);
2068
2069     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2070     return TRUE;
2071 }
2072
2073 gboolean
2074 sat_panel_expose(GtkWidget *widget, GdkEventExpose *event)
2075 {
2076     gchar *tmp = NULL;
2077     gint x, y;
2078     printf("%s()\n", __PRETTY_FUNCTION__);
2079
2080     draw_sat_info(widget,
2081         0, 0,
2082         widget->allocation.width,
2083         widget->allocation.height,
2084         FALSE);
2085
2086     /* Sat View/In Use */
2087     tmp = g_strdup_printf("%d/%d", _gps.satinuse, _gps.satinview);
2088     pango_layout_set_text(_sat_panel_layout, tmp, -1);
2089     pango_layout_set_alignment(_sat_panel_layout, PANGO_ALIGN_LEFT);
2090     gdk_draw_layout(widget->window,
2091         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2092         20, 2,
2093         _sat_panel_layout);
2094     g_free(tmp);
2095
2096     switch(_gps.fix)
2097     {
2098         case 2:
2099         case 3: tmp = g_strdup_printf("%dD fix", _gps.fix); break;
2100         default: tmp = g_strdup_printf("nofix"); break;
2101     }
2102     pango_layout_set_text(_sat_panel_layout, tmp, -1);
2103     pango_layout_set_alignment(_sat_panel_layout, PANGO_ALIGN_RIGHT);
2104     pango_layout_get_pixel_size(_sat_panel_layout, &x, &y);
2105     gdk_draw_layout(widget->window,
2106         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2107         widget->allocation.width - 20 - x, 2,
2108         _sat_panel_layout);
2109     g_free(tmp);
2110
2111     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2112     return TRUE;
2113 }
2114
2115 gboolean
2116 heading_panel_expose(GtkWidget *widget, GdkEventExpose *event)
2117 {
2118     gint size, xoffset, yoffset, i, x, y;
2119     gint dir;
2120     gfloat tmp;
2121     gchar *text;
2122     printf("%s()\n", __PRETTY_FUNCTION__);
2123
2124     size = MIN(widget->allocation.width, widget->allocation.height);
2125     if(widget->allocation.width > widget->allocation.height)
2126     {
2127         xoffset = (widget->allocation.width - widget->allocation.height) / 2;
2128         yoffset = 0;
2129     }
2130     else
2131     {
2132         xoffset = 0;
2133         yoffset = (widget->allocation.height - widget->allocation.width) / 2;
2134     }
2135     pango_font_description_set_size(_heading_panel_fontdesc,12*PANGO_SCALE);
2136     pango_layout_set_font_description(_heading_panel_layout,
2137             _heading_panel_fontdesc);
2138     pango_layout_set_alignment(_heading_panel_layout, PANGO_ALIGN_CENTER);
2139
2140     text = g_strdup_printf("%3.0f°", _gps.heading);
2141     pango_layout_set_text(_heading_panel_layout, text, -1);
2142     pango_layout_get_pixel_size(_heading_panel_layout, &x, &y);
2143
2144     gdk_draw_layout(widget->window,
2145         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2146         xoffset + size/2 - x/2,
2147         yoffset + size - y - 2, _heading_panel_layout);
2148     g_free(text);
2149
2150     gdk_draw_arc (widget->window,
2151         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2152         FALSE,
2153         xoffset, yoffset + size/2,
2154         size, size,
2155         0, 64 * 180);
2156
2157     /* Simple arrow for heading*/
2158     gdk_draw_line(widget->window,
2159         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2160         xoffset + size/2 + 3,
2161         yoffset + size - y - 5,
2162         xoffset + size/2,
2163         yoffset + size/2 + 5);
2164
2165     gdk_draw_line(widget->window,
2166         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2167         xoffset + size/2 - 3,
2168         yoffset + size - y - 5,
2169         xoffset + size/2,
2170         yoffset + size/2 + 5);
2171
2172     gdk_draw_line(widget->window,
2173         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2174         xoffset + size/2 - 3,
2175         yoffset + size - y - 5,
2176         xoffset + size/2,
2177         yoffset + size - y - 8);
2178
2179     gdk_draw_line(widget->window,
2180         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2181         xoffset + size/2 + 3,
2182         yoffset + size - y - 5,
2183         xoffset + size/2,
2184         yoffset + size - y - 8);
2185
2186     gint angle[5] = {-90,-45,0,45,90};
2187     gint fsize[5] = {0,4,10,4,0};
2188     for(i = 0; i < 5; i++)
2189     {
2190         dir = (gint)(_gps.heading/45)*45 + angle[i];
2191
2192         switch(dir)
2193         {
2194             case   0:
2195             case 360: text = g_strdup("N"); break;
2196             case  45:
2197             case 405:
2198                 text = g_strdup("NE"); break;
2199             case  90:
2200                 text = g_strdup("E"); break;
2201             case 135:
2202                 text = g_strdup("SE"); break;
2203             case 180:
2204                 text = g_strdup("S"); break;
2205             case 225:
2206                 text = g_strdup("SW"); break;
2207             case 270:
2208             case -90:
2209                 text = g_strdup("W"); break;
2210             case 315:
2211             case -45:
2212                 text = g_strdup("NW"); break;
2213             default :
2214                 text = g_strdup("??");
2215                 break;
2216         }
2217
2218         tmp = deg2rad(dir - _gps.heading);
2219         gdk_draw_line(widget->window,
2220             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2221             xoffset + size/2 + ((size/2 - 5) * sinf(tmp)),
2222             yoffset + size - ((size/2 - 5) * cosf(tmp)),
2223             xoffset + size/2 + ((size/2 + 5) * sinf(tmp)),
2224             yoffset + size - ((size/2 + 5) * cosf(tmp)));
2225
2226         x = fsize[i];
2227         if(abs((gint)(_gps.heading/45)*45 - _gps.heading)
2228                 > abs((gint)(_gps.heading/45)*45 + 45 - _gps.heading)
2229                 && (i > 0))
2230             x = fsize[i - 1];
2231
2232         pango_font_description_set_size(_heading_panel_fontdesc,
2233                 (10 + x)*PANGO_SCALE);
2234         pango_layout_set_font_description(_heading_panel_layout,
2235                 _heading_panel_fontdesc);
2236         pango_layout_set_text(_heading_panel_layout, text, -1);
2237         pango_layout_get_pixel_size(_heading_panel_layout, &x, &y);
2238         x = xoffset + size/2 + ((size/2 + 15) * sinf(tmp)) - x/2,
2239         y = yoffset + size - ((size/2 + 15) * cosf(tmp)) - y/2,
2240
2241         gdk_draw_layout(widget->window,
2242             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
2243             x, y, _heading_panel_layout);
2244         g_free(text);
2245     }
2246
2247     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2248     return TRUE;
2249 }
2250
2251 gboolean
2252 map_cb_expose(GtkWidget *widget, GdkEventExpose *event)
2253 {
2254     gint i;
2255     printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
2256             event->area.x, event->area.y,
2257             event->area.width, event->area.height);
2258
2259     gdk_draw_drawable(
2260             _map_widget->window,
2261             _gc[COLORABLE_MARK],
2262             _map_pixmap,
2263             event->area.x - _map_offset_devx, event->area.y - _map_offset_devy,
2264             event->area.x, event->area.y,
2265             event->area.width, event->area.height);
2266
2267     /* Draw the mark. */
2268     if((((unsigned)(_mark_bufx1 + _draw_width)
2269                 <= _view_width_pixels+2*_draw_width)
2270              &&((unsigned)(_mark_bufy1 + _draw_width)
2271                  <= _view_height_pixels+2*_draw_width))
2272         || (((unsigned)(_mark_bufx2 + _draw_width)
2273                  <= _view_width_pixels+2*_draw_width)
2274              &&((unsigned)(_mark_bufy2 + _draw_width)
2275                  <= _view_height_pixels+2*_draw_width)))
2276     {
2277         gdk_draw_arc(
2278                 _map_widget->window,
2279                 _gps_state == RCVR_FIXED
2280                     ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD],
2281                 FALSE, /* not filled. */
2282                 _mark_bufx1 - _draw_width + _map_offset_devx,
2283                 _mark_bufy1 - _draw_width + _map_offset_devy,
2284                 2 * _draw_width, 2 * _draw_width,
2285                 0, 360 * 64);
2286         gdk_draw_line(
2287                 _map_widget->window,
2288                 _gps_state == RCVR_FIXED
2289                     ? (_show_velvec
2290                         ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK])
2291                     : _gc[COLORABLE_MARK_OLD],
2292                 _mark_bufx1 + _map_offset_devx,
2293                 _mark_bufy1 + _map_offset_devy,
2294                 _mark_bufx2 + _map_offset_devx,
2295                 _mark_bufy2 + _map_offset_devy);
2296     }
2297
2298     /* draw zoom box if so wanted */
2299     if(_show_zoomlevel) {
2300         gchar *buffer = g_strdup_printf("%d", _zoom);
2301         gdk_draw_rectangle(_map_widget->window,
2302                 _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2303                 TRUE,
2304                 _zoom_rect.x, _zoom_rect.y,
2305                 _zoom_rect.width, _zoom_rect.height);
2306         gdk_draw_rectangle(_map_widget->window,
2307                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2308                 FALSE,
2309                 _zoom_rect.x, _zoom_rect.y,
2310                 _zoom_rect.width, _zoom_rect.height);
2311         pango_layout_set_text(_zoom_layout, buffer, -1);
2312         gdk_draw_layout(_map_widget->window,
2313                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2314                 _zoom_rect.x + _zoom_rect.width / 2,
2315                 _zoom_rect.y, _zoom_layout);
2316     }
2317
2318     /* Draw scale, if necessary. */
2319     if(_show_scale)
2320     {
2321         gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
2322         if(event->area.width && event->area.height)
2323         {
2324             gdk_draw_rectangle(_map_widget->window,
2325                     _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2326                     TRUE,
2327                     _scale_rect.x, _scale_rect.y,
2328                     _scale_rect.width, _scale_rect.height);
2329             gdk_draw_rectangle(_map_widget->window,
2330                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2331                     FALSE,
2332                     _scale_rect.x, _scale_rect.y,
2333                     _scale_rect.width, _scale_rect.height);
2334
2335             /* Now calculate and draw the distance. */
2336             {
2337                 gchar buffer[16];
2338                 gfloat distance;
2339                 gdouble lat1, lon1, lat2, lon2;
2340                 gint width;
2341
2342                 unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4),
2343                         _center.unity, lat1, lon1);
2344                 unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4),
2345                         _center.unity, lat2, lon2);
2346                 distance = calculate_distance(lat1, lon1, lat2, lon2)
2347                     * UNITS_CONVERT[_units];
2348
2349                 if(distance < 1.f)
2350                     snprintf(buffer, sizeof(buffer), "%0.2f %s", distance,
2351                             UNITS_ENUM_TEXT[_units]);
2352                 else if(distance < 10.f)
2353                     snprintf(buffer, sizeof(buffer), "%0.1f %s", distance,
2354                             UNITS_ENUM_TEXT[_units]);
2355                 else
2356                     snprintf(buffer, sizeof(buffer), "%0.f %s", distance,
2357                             UNITS_ENUM_TEXT[_units]);
2358                 pango_layout_set_text(_scale_layout, buffer, -1);
2359
2360                 pango_layout_get_pixel_size(_scale_layout, &width, NULL);
2361
2362                 /* Draw the layout itself. */
2363                 gdk_draw_layout(_map_widget->window,
2364                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2365                     _scale_rect.x + (_scale_rect.width - width) / 2,
2366                     _scale_rect.y, _scale_layout);
2367
2368                 /* Draw little hashes on the ends. */
2369                 gdk_draw_line(_map_widget->window,
2370                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2371                     _scale_rect.x + 4,
2372                     _scale_rect.y + _scale_rect.height / 2 - 4,
2373                     _scale_rect.x + 4,
2374                     _scale_rect.y + _scale_rect.height / 2 + 4);
2375                 gdk_draw_line(_map_widget->window,
2376                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2377                     _scale_rect.x + 4,
2378                     _scale_rect.y + _scale_rect.height / 2,
2379                     _scale_rect.x + (_scale_rect.width - width) / 2 - 4,
2380                     _scale_rect.y + _scale_rect.height / 2);
2381                 gdk_draw_line(_map_widget->window,
2382                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2383                     _scale_rect.x + _scale_rect.width - 4,
2384                     _scale_rect.y + _scale_rect.height / 2 - 4,
2385                     _scale_rect.x + _scale_rect.width - 4,
2386                     _scale_rect.y + _scale_rect.height / 2 + 4);
2387                 gdk_draw_line(_map_widget->window,
2388                     _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2389                     _scale_rect.x + _scale_rect.width - 4,
2390                     _scale_rect.y + _scale_rect.height / 2,
2391                     _scale_rect.x + (_scale_rect.width + width) / 2 + 4,
2392                     _scale_rect.y + _scale_rect.height / 2);
2393             }
2394         }
2395     }
2396
2397     /* Draw the compass rose, if necessary. */
2398     if(_show_comprose)
2399     {
2400         GdkPoint points[3];
2401         gint offsetx, offsety;
2402         gfloat x, y;
2403
2404         offsetx = _comprose_rect.x;
2405         offsety = _comprose_rect.y;
2406
2407         gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 0, 12);
2408         points[0].x = offsetx + x + 0.5f; points[0].y = offsety + y + 0.5f;
2409         gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 20, 30);
2410         points[1].x = offsetx + x + 0.5f; points[1].y = offsety + y + 0.5f;
2411         gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, 0, -45);
2412         points[2].x = offsetx + x + 0.5f; points[2].y = offsety + y + 0.5f;
2413         gdk_pixbuf_rotate_vector(&x, &y, _map_rotate_matrix, -20, 30);
2414         points[3].x = offsetx + x + 0.5f; points[3].y = offsety + y + 0.5f;
2415
2416         gdk_draw_polygon(_map_widget->window,
2417                 _map_widget->style->bg_gc[GTK_STATE_ACTIVE],
2418                 TRUE, /* FILLED */
2419                 points, 4);
2420         gdk_draw_polygon(_map_widget->window,
2421                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2422                 FALSE, /* NOT FILLED */
2423                 points, 4);
2424
2425         gdk_draw_layout(_map_widget->window,
2426                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2427                 _comprose_rect.x - _comprose_rect.width / 2 + 1,
2428                 _comprose_rect.y - _comprose_rect.height / 2 - 4,
2429                 _comprose_layout);
2430     }
2431
2432     /* Draw a stopwatch if we're redrawing the map. */
2433     for(i = _redraw_count - 1; i >= 0; i--)
2434     {
2435         gdk_draw_pixbuf(
2436                 _map_widget->window,
2437                 _map_widget->style->fg_gc[GTK_STATE_ACTIVE],
2438                 _redraw_wait_icon,
2439                 0, 0,
2440                 _redraw_wait_bounds.x + i
2441                         * HOURGLASS_SEPARATION,
2442                 _redraw_wait_bounds.y,
2443                 _redraw_wait_bounds.width,
2444                 _redraw_wait_bounds.height,
2445                 GDK_RGB_DITHER_NONE, 0, 0);
2446     }
2447
2448     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2449     return TRUE;
2450 }
2451
2452 static void
2453 latlon_cb_copy_clicked(GtkWidget *widget, LatlonDialog *lld) {
2454     gchar buffer[42];
2455
2456     snprintf(buffer, sizeof(buffer),
2457             "%s %s",
2458             gtk_label_get_text(GTK_LABEL(lld->lat)),
2459             gtk_label_get_text(GTK_LABEL(lld->lon)));
2460
2461     gtk_clipboard_set_text(
2462             gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), buffer, -1);
2463 }
2464
2465 static void
2466 latlon_cb_fmt_changed(GtkWidget *widget, LatlonDialog *lld) {
2467   DegFormat fmt;
2468
2469   fmt = gtk_combo_box_get_active(GTK_COMBO_BOX(lld->fmt_combo));
2470
2471   {
2472     gint old = _degformat; /* augh... */
2473     gchar buffer[LL_FMT_LEN];
2474
2475     _degformat = fmt;
2476     lat_format(lld->glat, buffer);
2477     gtk_label_set_label(GTK_LABEL(lld->lat), buffer);
2478     lon_format(lld->glon, buffer);
2479     gtk_label_set_label(GTK_LABEL(lld->lon), buffer);
2480     _degformat = old;
2481   }
2482 }
2483
2484 gboolean
2485 latlon_dialog(gdouble lat, gdouble lon)
2486 {
2487     LatlonDialog lld;
2488     GtkWidget *dialog;
2489     GtkWidget *table;
2490     GtkWidget *label;
2491     GtkWidget *txt_lat;
2492     GtkWidget *txt_lon;
2493     GtkWidget *cmb_format;
2494     GtkWidget *btn_copy = NULL;
2495     printf("%s()\n", __PRETTY_FUNCTION__);
2496
2497     dialog = gtk_dialog_new_with_buttons(_("Show Position"),
2498             GTK_WINDOW(_window), GTK_DIALOG_MODAL,
2499             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2500             NULL);
2501
2502     /* Set the lat/lon strings. */
2503     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2504             table = gtk_table_new(5, 2, FALSE), TRUE, TRUE, 0);
2505
2506     gtk_table_attach(GTK_TABLE(table),
2507             label = gtk_label_new(_("Lat")),
2508             0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2509     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2510     gtk_table_attach(GTK_TABLE(table),
2511             txt_lat = gtk_label_new(""),
2512             1, 2, 0, 1, GTK_FILL, 0, 2, 4);
2513     gtk_misc_set_alignment(GTK_MISC(txt_lat), 1.f, 0.5f);
2514
2515     gtk_table_attach(GTK_TABLE(table),
2516             label = gtk_label_new(_("Lon")),
2517             0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2518     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2519     gtk_table_attach(GTK_TABLE(table),
2520             txt_lon = gtk_label_new(""),
2521             1, 2, 1, 2, GTK_FILL, 0, 2, 4);
2522     gtk_misc_set_alignment(GTK_MISC(txt_lon), 1.f, 0.5f);
2523
2524     gtk_table_attach(GTK_TABLE(table),
2525             label = gtk_label_new(_("Format")),
2526             0, 1, 2, 3, GTK_FILL, 0, 2, 4);
2527     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2528     gtk_table_attach(GTK_TABLE(table),
2529             label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f),
2530             1, 2, 2, 3, GTK_FILL, 0, 2, 4);
2531     gtk_container_add(GTK_CONTAINER(label),
2532             cmb_format = gtk_combo_box_new_text());
2533     gtk_table_attach(GTK_TABLE(table),
2534             btn_copy = gtk_button_new_with_label(_("Copy")),
2535             0, 2, 3, 4, GTK_FILL, 0, 2, 4);
2536
2537     /* Lat/Lon */
2538     {
2539       gchar buffer[LL_FMT_LEN];
2540
2541       lat_format(lat, buffer);
2542       gtk_label_set_label(GTK_LABEL(txt_lat), buffer);
2543       lat_format(lon, buffer);
2544       gtk_label_set_label(GTK_LABEL(txt_lon), buffer);
2545     }
2546
2547     /* Fill in formats */
2548     {
2549       int i;
2550
2551       for(i = 0; i < DEG_FORMAT_ENUM_COUNT; i++) {
2552           gtk_combo_box_append_text(GTK_COMBO_BOX(cmb_format),
2553                   DEG_FORMAT_ENUM_TEXT[i]);
2554       }
2555       gtk_combo_box_set_active(GTK_COMBO_BOX(cmb_format), _degformat);
2556     }
2557
2558
2559     /* setup cb context */
2560     lld.fmt_combo = cmb_format;
2561     lld.glat = lat;
2562     lld.glon = lon;
2563     lld.lat = txt_lat;
2564     lld.lon = txt_lon;
2565
2566     /* Connect Signals */
2567     g_signal_connect(G_OBJECT(cmb_format), "changed",
2568                     G_CALLBACK(latlon_cb_fmt_changed), &lld);
2569     g_signal_connect(G_OBJECT(btn_copy), "clicked",
2570                     G_CALLBACK(latlon_cb_copy_clicked), &lld);
2571
2572     gtk_widget_show_all(dialog);
2573
2574     gtk_dialog_run(GTK_DIALOG(dialog));
2575     gtk_widget_hide(dialog);
2576
2577     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2578     return TRUE;
2579 }
2580
2581
2582 /**
2583  * This is a multi-purpose function for allowing the user to select a file
2584  * for either reading or writing.  If chooser_action is
2585  * GTK_FILE_CHOOSER_ACTION_OPEN, then bytes_out and size_out must be
2586  * non-NULL.  If chooser_action is GTK_FILE_CHOOSER_ACTION_SAVE, then
2587  * handle_out must be non-NULL.  Either dir or file (or both) can be NULL.
2588  * This function returns TRUE if a file was successfully opened.
2589  */
2590 gboolean
2591 display_open_file(GtkWindow *parent, gchar **bytes_out,
2592         GnomeVFSHandle **handle_out, gint *size_out,
2593         gchar **dir, gchar **file, GtkFileChooserAction chooser_action)
2594 {
2595     GtkWidget *dialog;
2596     gboolean success = FALSE;
2597     printf("%s()\n", __PRETTY_FUNCTION__);
2598
2599     dialog = hildon_file_chooser_dialog_new(parent, chooser_action);
2600
2601     if(dir && *dir)
2602         gtk_file_chooser_set_current_folder_uri(
2603                 GTK_FILE_CHOOSER(dialog), *dir);
2604     if(file && *file)
2605     {
2606         gtk_file_chooser_set_uri(
2607                 GTK_FILE_CHOOSER(dialog), *file);
2608         if(chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE)
2609         {
2610             /* Work around a bug in HildonFileChooserDialog. */
2611             gchar *basename = g_path_get_basename(*file);
2612             g_object_set(G_OBJECT(dialog), "autonaming", FALSE, NULL);
2613             gtk_file_chooser_set_current_name(
2614                     GTK_FILE_CHOOSER(dialog), basename);
2615             g_free(basename);
2616         }
2617     }
2618
2619     gtk_widget_show_all(dialog);
2620
2621     while(!success && gtk_dialog_run(GTK_DIALOG(dialog))==GTK_RESPONSE_OK)
2622     {
2623         gchar *file_uri_str;
2624         GnomeVFSResult vfs_result;
2625
2626         /* Get the selected filename. */
2627         file_uri_str = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
2628
2629         if((chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN
2630                 && (GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
2631                         file_uri_str, size_out, bytes_out))))
2632                 || (chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE
2633                     && GNOME_VFS_OK != (vfs_result = gnome_vfs_create(
2634                             handle_out, file_uri_str,
2635                             GNOME_VFS_OPEN_WRITE, FALSE, 0664))))
2636         {
2637             gchar buffer[BUFFER_SIZE];
2638             snprintf(buffer, sizeof(buffer),
2639                     "%s:\n%s", chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN
2640                                 ? _("Failed to open file for reading")
2641                                 : _("Failed to open file for writing"),
2642                     gnome_vfs_result_to_string(vfs_result));
2643             popup_error(dialog, buffer);
2644         }
2645         else
2646             success = TRUE;
2647
2648         g_free(file_uri_str);
2649     }
2650
2651     if(success)
2652     {
2653         /* Success!. */
2654         if(dir)
2655         {
2656             g_free(*dir);
2657             *dir = gtk_file_chooser_get_current_folder_uri(
2658                     GTK_FILE_CHOOSER(dialog));
2659         }
2660
2661         /* If desired, save the file for later. */
2662         if(file)
2663         {
2664             g_free(*file);
2665             *file = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
2666         }
2667     }
2668
2669     gtk_widget_destroy(dialog);
2670
2671     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, success);
2672     return success;
2673 }
2674
2675 void
2676 display_init()
2677 {
2678     PangoContext *pango_context;
2679     PangoFontDescription *pango_font;
2680     GdkColor color;
2681     printf("%s()\n", __PRETTY_FUNCTION__);
2682
2683     /* Cache some pango and GCs for drawing. */
2684     pango_context = gtk_widget_get_pango_context(_map_widget);
2685     _scale_layout = pango_layout_new(pango_context);
2686     pango_font = pango_font_description_new();
2687     pango_font_description_set_size(pango_font, 12 * PANGO_SCALE);
2688     pango_layout_set_font_description(_scale_layout, pango_font);
2689
2690     /* zoom box */
2691     pango_context = gtk_widget_get_pango_context(_map_widget);
2692     _zoom_layout = pango_layout_new(pango_context);
2693     pango_font = pango_font_description_new();
2694     pango_font_description_set_size(pango_font, 12 * PANGO_SCALE);
2695     pango_layout_set_font_description(_zoom_layout, pango_font);
2696     pango_layout_set_alignment(_zoom_layout, PANGO_ALIGN_CENTER);
2697
2698     /* compose rose */
2699     pango_context = gtk_widget_get_pango_context(_map_widget);
2700     _comprose_layout = pango_layout_new(pango_context);
2701     pango_font = pango_font_description_new();
2702     pango_font_description_set_size(pango_font, 16 * PANGO_SCALE);
2703     pango_font_description_set_weight(pango_font, PANGO_WEIGHT_BOLD);
2704     pango_layout_set_font_description(_comprose_layout, pango_font);
2705     pango_layout_set_alignment(_comprose_layout, PANGO_ALIGN_CENTER);
2706     pango_layout_set_text(_comprose_layout, "N", -1);
2707     {
2708         PangoRectangle rect;
2709         pango_layout_get_pixel_extents(_comprose_layout, &rect, NULL);
2710         _comprose_rect.width = rect.width + 3;
2711         _comprose_rect.height = rect.height + 3;
2712     }
2713
2714     /* speed limit */
2715     _speed_limit_gc1 = gdk_gc_new (_map_widget->window);
2716     color.red = 0xffff;
2717     color.green = 0;
2718     color.blue = 0;
2719     gdk_gc_set_rgb_fg_color(_speed_limit_gc1, &color);
2720     color.red = 0;
2721     color.green = 0;
2722     color.blue = 0;
2723     _speed_limit_gc2 = gdk_gc_new(_map_widget->window);
2724     gdk_gc_set_rgb_fg_color(_speed_limit_gc2, &color);
2725     pango_context = gtk_widget_get_pango_context(_map_widget);
2726     _speed_limit_layout = pango_layout_new(pango_context);
2727     pango_font = pango_font_description_new();
2728     pango_font_description_set_size(pango_font, 64 * PANGO_SCALE);
2729     pango_layout_set_font_description(_speed_limit_layout,
2730             pango_font);
2731     pango_layout_set_alignment(_speed_limit_layout, PANGO_ALIGN_CENTER);
2732
2733     /* draw_sat_info() */
2734     _sat_info_gc1 = gdk_gc_new(_map_widget->window);
2735     color.red = 0;
2736     color.green = 0;
2737     color.blue = 0;
2738     gdk_gc_set_rgb_fg_color(_sat_info_gc1, &color);
2739     color.red = 0;
2740     color.green = 0;
2741     color.blue = 0xffff;
2742     _sat_info_gc2 = gdk_gc_new(_map_widget->window);
2743     gdk_gc_set_rgb_fg_color(_sat_info_gc2, &color);
2744     pango_context = gtk_widget_get_pango_context(_map_widget);
2745     _sat_info_layout = pango_layout_new(pango_context);
2746     pango_font = pango_font_description_new();
2747     pango_font_description_set_family(pango_font,"Sans Serif");
2748     pango_font_description_set_size(pango_font, 8*PANGO_SCALE);
2749     pango_layout_set_font_description(_sat_info_layout, pango_font);
2750     pango_layout_set_alignment(_sat_info_layout, PANGO_ALIGN_CENTER);
2751
2752     /* sat_panel_expose() */
2753     pango_context = gtk_widget_get_pango_context(_map_widget);
2754     _sat_panel_layout = pango_layout_new(pango_context);
2755     pango_font = pango_font_description_new();
2756     pango_font_description_set_family(pango_font,"Sans Serif");
2757     pango_font_description_set_size(pango_font, 14*PANGO_SCALE);
2758     pango_layout_set_font_description (_sat_panel_layout, pango_font);
2759
2760     /* heading_panel_expose() */
2761     pango_context = gtk_widget_get_pango_context(_map_widget);
2762     _heading_panel_layout = pango_layout_new(pango_context);
2763     _heading_panel_fontdesc =  pango_font_description_new();
2764     pango_font_description_set_family(_heading_panel_fontdesc, "Sans Serif");
2765
2766     /* draw_sat_details() */
2767     pango_context = gtk_widget_get_pango_context(_map_widget);
2768     _sat_details_layout = pango_layout_new(pango_context);
2769     pango_font = pango_font_description_new();
2770     pango_font_description_set_family(pango_font,"Sans Serif");
2771     pango_font_description_set_size(pango_font, 10*PANGO_SCALE);
2772     pango_layout_set_font_description(_sat_details_layout,
2773             pango_font);
2774     pango_layout_set_alignment(_sat_details_layout, PANGO_ALIGN_CENTER);
2775
2776     /* sat_details_panel_expose() */
2777     pango_context = gtk_widget_get_pango_context(_map_widget);
2778     _sat_details_expose_layout = pango_layout_new(pango_context);
2779     pango_font = pango_font_description_new();
2780     pango_font_description_set_family(
2781             pango_font,"Sans Serif");
2782     pango_layout_set_alignment(_sat_details_expose_layout,
2783             PANGO_ALIGN_CENTER);
2784     pango_font_description_set_size(pango_font,
2785             14*PANGO_SCALE);
2786     pango_layout_set_font_description(_sat_details_expose_layout,
2787             pango_font);
2788
2789     /* Load the _redraw_wait_icon. */
2790     {
2791         GError *error = NULL;
2792         gchar *icon_path = "/usr/share/icons/hicolor/scalable/hildon"
2793                            "/maemo-mapper-wait.png";
2794         _redraw_wait_bounds.x = 0;
2795         _redraw_wait_bounds.y = 0;
2796         _redraw_wait_icon = gdk_pixbuf_new_from_file(icon_path, &error);
2797         if(!_redraw_wait_icon || error)
2798         {
2799             g_printerr("Error parsing pixbuf: %s\n",
2800                     error ? error->message : icon_path);
2801             _redraw_wait_bounds.width = 0;
2802             _redraw_wait_bounds.height = 0;
2803             _redraw_wait_icon = NULL;
2804         }
2805         else
2806         {
2807             _redraw_wait_bounds.width
2808                 = gdk_pixbuf_get_width(_redraw_wait_icon);
2809             _redraw_wait_bounds.height
2810                 = gdk_pixbuf_get_height(_redraw_wait_icon);
2811         }
2812     }
2813
2814     g_signal_connect(G_OBJECT(_map_widget), "configure_event",
2815             G_CALLBACK(map_cb_configure), NULL);
2816
2817     g_signal_connect(G_OBJECT(_map_widget), "expose_event",
2818             G_CALLBACK(map_cb_expose), NULL);
2819     g_signal_connect(G_OBJECT(_sat_panel), "expose_event",
2820             G_CALLBACK(sat_panel_expose), NULL);
2821     g_signal_connect(G_OBJECT(_heading_panel), "expose_event",
2822             G_CALLBACK(heading_panel_expose), NULL);
2823
2824     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2825 }