]> err.no Git - mapper/blob - src/map.c
cf070b6b4b2c4e0657862c25cd90d0b9520a2952
[mapper] / src / map.c
1 #include <config.h>
2
3 #define _GNU_SOURCE
4
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <strings.h>
9 #include <stddef.h>
10 #include <locale.h>
11 #include <math.h>
12 #include <errno.h>
13 #include <sys/wait.h>
14 #include <glib/gstdio.h>
15 #include <gtk/gtk.h>
16 #include <fcntl.h>
17 #include <libgnomevfs/gnome-vfs.h>
18 #include <gconf/gconf-client.h>
19 #include <libxml/parser.h>
20 #include <curl/multi.h>
21 #include <libintl.h>
22 #include <locale.h>
23 #include <sqlite3.h>
24
25 #include "hildon-mapper.h"
26
27 #include "utils.h"
28 #include "map.h"
29 #include "osm.h"
30 #include "db.h"
31 #include "osm-db.h"
32 #include "poi.h"
33 #include "route.h"
34 #include "track.h"
35 #include "gps.h"
36 #include "bt.h"
37 #include "mapper-types.h"
38 #include "ui-common.h"
39 #include "settings.h"
40 #include "latlon.h"
41 #include "gpx.h"
42 #include "map-download.h"
43
44 Point _min_center = { -1, -1 };
45 Point _max_center = { -1, -1 };
46 Point _focus = { -1, -1 };
47
48 /** The "base tile" is the upper-left tile in the pixmap. */
49 guint _base_tilex = -5;
50 guint _base_tiley = -5;
51
52 guint _zoom = 3;                /* zoom level, from 0 to MAX_ZOOM. */
53 Point _center = { -1, -1 };     /* current center location, X. */
54
55 CenterMode _center_mode = CENTER_LEAD;
56
57 static guint press[2] = { 0, 0 };
58 static guint release[2] = { 0, 0 };
59 static guint before[2] = { 0, 0 };
60 static guint _id = 0;
61
62 /** VARIABLES FOR ACCESSING THE LOCATION/BOUNDS OF THE CURRENT MARK. */
63 static gint _mark_x1;
64 static gint _mark_x2;
65 static gint _mark_y1;
66 static gint _mark_y2;
67 static gint _mark_minx;
68 static gint _mark_miny;
69 static gint _mark_width;
70 static gint _mark_height;
71
72 static gint zoom_timeout_sid=0;
73 static gint map_mode=0;
74
75 static osm_location map_loc = {NULL, NULL, NULL, FALSE, FALSE, 0, 0, 0.0, 0.0 };
76
77 typedef struct _map_tile_rdata map_tile_rdata;
78 struct _map_tile_rdata {
79         guint tilex, tiley, zoom;
80         guint destx, desty;
81 };
82
83 void map_render_paths();
84 void map_force_redraw();
85 static void map_update_location(gint x, gint y, gboolean force);
86 void map_draw_position_icon(Position *pos);
87
88 gboolean 
89 map_cb_configure(GtkWidget * widget, GdkEventConfigure * event)
90 {
91 _screen_width_pixels = _map_widget->allocation.width;
92 _screen_height_pixels = _map_widget->allocation.height;
93 _screen_grids_halfwidth = pixel2grid(_screen_width_pixels) / 2;
94 _screen_grids_halfheight = pixel2grid(_screen_height_pixels) / 2;
95
96 /* Set _scale_rect. */
97 _scale_rect.x = (_screen_width_pixels - SCALE_WIDTH) / 2;
98 _scale_rect.width = SCALE_WIDTH;
99 pango_layout_set_text(_scale_layout, "0", -1);
100 pango_layout_get_pixel_size(_scale_layout, NULL, &_scale_rect.height);
101 _scale_rect.y = _screen_height_pixels - _scale_rect.height - 1;
102
103 MACRO_RECALC_FOCUS_BASE();
104 MACRO_RECALC_FOCUS_SIZE();
105
106 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
107 _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
108 _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
109 _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
110
111 map_center_unit(_center.unitx, _center.unity);
112
113 return TRUE;
114 }
115
116 /**
117  * Draw the current mark (representing the current GPS location) onto
118  * _map_widget.  This method does not queue the draw area.
119  */
120 static void 
121 map_draw_mark()
122 {
123 gdk_draw_arc(_map_widget->window, _conn_state == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE,
124      _mark_x1 - _draw_width, _mark_y1 - _draw_width, 
125         2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
126 gdk_draw_line(_map_widget->window,
127       _conn_state == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
128       _mark_x1, _mark_y1, _mark_x2, _mark_y2);
129 }
130
131 /**
132  * "Set" the mark, which translates the current GPS position into on-screen
133  * units in preparation for drawing the mark with map_draw_mark().
134  */
135 static void 
136 map_set_mark()
137 {
138 _mark_x1 = unit2x(_pos.unitx);
139 _mark_y1 = unit2y(_pos.unity);
140 _mark_x2 = _mark_x1 + (_show_velvec ? _vel_offsetx : 0);
141 _mark_y2 = _mark_y1 + (_show_velvec ? _vel_offsety : 0);
142 _mark_minx = MIN(_mark_x1, _mark_x2) - (2 * _draw_width);
143 _mark_miny = MIN(_mark_y1, _mark_y2) - (2 * _draw_width);
144 _mark_width = abs(_mark_x1 - _mark_x2) + (4 * _draw_width);
145 _mark_height = abs(_mark_y1 - _mark_y2) + (4 * _draw_width);
146 }
147
148 /**
149  * Do an in-place scaling of a pixbuf's pixels at the given ratio from the
150  * given source location.  It would have been nice if gdk_pixbuf provided
151  * this method, but I guess it's not general-purpose enough.
152  */
153 static void
154 map_pixbuf_scale_inplace(GdkPixbuf * pixbuf, guint ratio_p2, guint src_x, guint src_y)
155 {
156         guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
157         guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
158         guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
159         guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
160
161         vprintf("%s(%d, %d, %d)\n", __PRETTY_FUNCTION__, ratio_p2, src_x, src_y);
162
163         /* Sweep through the entire dest area, copying as necessary, but
164          * DO NOT OVERWRITE THE SOURCE AREA.  We'll copy it afterward. */
165         do {
166                 guint src_dim = dest_dim >> ratio_p2;
167                 guint src_endx = src_x - dest_x + src_dim;
168                 gint x, y;
169
170                 for (y = dest_dim - 1; y >= 0; y--) {
171                         guint src_offset_y, dest_offset_y;
172
173                         src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
174                         dest_offset_y = (dest_y + y) * rowstride;
175                         x = dest_dim - 1;
176
177                         if ((unsigned)(dest_y + y - src_y) < src_dim
178                             && (unsigned)(dest_x + x - src_x) < src_dim)
179                                 x -= src_dim;
180
181                         for (; x >= 0; x--) {
182                                 guint src_offset, dest_offset, i;
183
184                                 src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
185                                 dest_offset = dest_offset_y + (dest_x + x) * n_channels;
186
187                                 pixels[dest_offset] = pixels[src_offset];
188                                 for (i = n_channels - 1; i; i--)
189                                         pixels[dest_offset + i] = pixels[src_offset + i];
190
191                                 if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
192                                         x -= src_dim;
193                         }
194                 }
195                 /* Reuse src_dim and src_endx to store new src_x and src_y. */
196                 src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
197                 src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
198                 dest_x = src_x;
199                 dest_y = src_y;
200                 src_x = src_dim;
201                 src_y = src_endx;
202         }
203         while ((dest_dim >>= ratio_p2) > 1);
204
205         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
206 }
207
208 /**
209  * Trim pixbufs that are bigger than tiles. (Those pixbufs result, when
210  * captions should be cut off.)
211  */
212 static GdkPixbuf *
213 pixbuf_trim(GdkPixbuf * pixbuf)
214 {
215 GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
216                    8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
217
218 gdk_pixbuf_copy_area(pixbuf,
219                      (gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
220                      (gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2, 
221                      TILE_SIZE_PIXELS,
222                      TILE_SIZE_PIXELS, mpixbuf, 0, 0);
223
224 g_object_unref(pixbuf);
225 return mpixbuf;
226 }
227
228 void
229 map_render_tile(guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
230 {
231 GdkPixbuf *pixbuf = NULL;
232 gchar buffer[BUFFER_SIZE];
233 GError *error = NULL;
234 gint zoff;
235
236 if (tilex < _world_size_tiles && tiley < _world_size_tiles) {
237         /* The tile is possible. */
238         for (zoff = (_curr_repo->double_size ? 1 : 0);
239              !pixbuf && (_zoom + zoff) <= MAX_ZOOM
240              && zoff <= TILE_SIZE_P2; zoff += 1) {
241                 snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg",
242                          _curr_repo->cache_dir, _zoom + zoff,
243                          (tilex >> zoff), (tiley >> zoff));
244
245                 pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
246                 if (error || !pixbuf) {
247                         /* Delete so we try again some other day. */
248                         g_unlink(buffer);       
249                         pixbuf = NULL;
250                         error = NULL;
251
252                         /* Download, if we should. */
253                         if (_auto_download && _curr_repo->type != REPOTYPE_NONE &&
254                             !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
255                                 if (!fast_fail)
256                                         map_initiate_download(tilex >> zoff,
257                                                               tiley >> zoff,
258                                                               _zoom + zoff,
259                                                               -INITIAL_DOWNLOAD_RETRIES);
260                                 fast_fail = TRUE;
261                         }
262                 } else {
263                         /* Check if we need to trim. */
264                         if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
265                                 pixbuf = pixbuf_trim(pixbuf);
266
267                         /* Check if we need to blit. */
268                         if (zoff) {
269                                 map_pixbuf_scale_inplace(pixbuf, zoff,
270                                                  (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
271                                                  (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
272                         }
273                 }
274         }
275 }
276
277 if (pixbuf) {
278         gdk_draw_pixbuf(_map_pixmap,
279                         _gc[COLORABLE_MARK],
280                         pixbuf, 0, 0, destx, desty,
281                         TILE_SIZE_PIXELS, TILE_SIZE_PIXELS,
282                         GDK_RGB_DITHER_NONE, 0, 0);
283         g_object_unref(pixbuf);
284 } else {
285         gdk_draw_rectangle(_map_pixmap, _map_widget->style->black_gc,
286                            TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
287 }
288 }
289
290 gboolean 
291 map_render_tile_idle(map_tile_rdata *mtr)
292 {
293 map_render_tile(mtr->tilex, mtr->tiley, mtr->destx, mtr->desty, FALSE);
294 gtk_widget_queue_draw_area(_map_widget, mtr->destx, mtr->desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
295
296 g_slice_free(map_tile_rdata, mtr);
297 return FALSE;
298 }
299
300 void
301 map_render_data(void)
302 {
303 if(_show_poi)
304         map_render_poi();
305 if(_show_tracks > 0)
306         map_render_paths();
307 if (_home.valid)
308         map_draw_position_icon(&_home);
309 }
310
311 /**
312  * Force a redraw of the entire _map_pixmap, including fetching the
313  * background maps from disk and redrawing the tracks on top of them.
314  */
315 void 
316 map_force_redraw()
317 {
318         guint new_x, new_y;
319         printf("%s()\n", __PRETTY_FUNCTION__);
320
321         for (new_y = 0; new_y < BUF_HEIGHT_TILES; ++new_y)
322                 for (new_x = 0; new_x < BUF_WIDTH_TILES; ++new_x) {
323                         map_render_tile(_base_tilex + new_x,
324                                         _base_tiley + new_y,
325                                         new_x * TILE_SIZE_PIXELS,
326                                         new_y * TILE_SIZE_PIXELS, FALSE);
327                 }
328         map_render_data();
329         MACRO_QUEUE_DRAW_AREA();
330
331         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
332 }
333
334 /**
335  * Set the current zoom level.  If the given zoom level is the same as the
336  * current zoom level, or if the new zoom is invalid
337  * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
338  */
339 void 
340 map_set_zoom(guint new_zoom)
341 {
342         printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom);
343
344         /* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
345          * condition also checks for new_zoom >= MIN_ZOOM. */
346         if (new_zoom > (MAX_ZOOM - 1))
347                 return;
348         if (new_zoom == _zoom)
349                 return;
350
351         _zoom = new_zoom / _curr_repo->view_zoom_steps
352             * _curr_repo->view_zoom_steps;
353         _world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
354
355         /* If we're leading, update the center to reflect new zoom level. */
356         MACRO_RECALC_CENTER(_center.unitx, _center.unity);
357
358         /* Update center bounds to reflect new zoom level. */
359         _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
360         _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
361         _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
362         _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
363
364         BOUND(_center.unitx, _min_center.unitx, _max_center.unitx);
365         BOUND(_center.unity, _min_center.unity, _max_center.unity);
366
367         _base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) _center.unitx))
368                                 - (gint) _screen_grids_halfwidth);
369         _base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
370                                 - _screen_grids_halfheight);
371
372         /* New zoom level, so we can't reuse the old buffer's pixels. */
373
374         /* Update state variables. */
375         MACRO_RECALC_OFFSET();
376         MACRO_RECALC_FOCUS_BASE();
377         MACRO_RECALC_FOCUS_SIZE();
378
379         map_set_mark();
380         map_force_redraw();
381
382         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
383 }
384
385 /**
386  * Center the view on the given unitx/unity.
387  */
388 void 
389 map_center_unit(guint new_center_unitx, guint new_center_unity)
390 {
391 gint new_base_tilex, new_base_tiley;
392 guint new_x, new_y;
393 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
394 g_printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, new_center_unitx, new_center_unity);
395
396 /* Assure that _center.unitx/y are bounded. */
397 BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx);
398 BOUND(new_center_unity, _min_center.unity, _max_center.unity);
399
400 _center.unitx = new_center_unitx;
401 _center.unity = new_center_unity;
402
403 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) _center.unitx))
404                            - (gint)_screen_grids_halfwidth);
405 new_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity))
406                            - _screen_grids_halfheight);
407
408 /* Same zoom level, so it's likely that we can reuse some of the old
409  * buffer's pixels. */
410
411 if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
412         /* If copying from old parts to new parts, we need to make sure we
413          * don't overwrite the old parts when copying, so set up new_x,
414          * new_y, old_x, old_y, iox, and ioy with that in mind. */
415         if (new_base_tiley < _base_tiley) {
416                 /* New is lower than old - start at bottom and go up. */
417                 new_y = BUF_HEIGHT_TILES - 1;
418                 ioy = -1;
419         } else {
420                 /* New is higher than old - start at top and go down. */
421                 new_y = 0;
422                 ioy = 1;
423         }
424         if (new_base_tilex < _base_tilex) {
425                 /* New is righter than old - start at right and go left. */
426                 base_new_x = BUF_WIDTH_TILES - 1;
427                 iox = -1;
428         } else {
429                 /* New is lefter than old - start at left and go right. */
430                 base_new_x = 0;
431                 iox = 1;
432         }
433
434         /* Iterate over the y tile values. */
435         old_y = new_y + new_base_tiley - _base_tiley;
436         base_old_x = base_new_x + new_base_tilex - _base_tilex;
437         _base_tilex = new_base_tilex;
438         _base_tiley = new_base_tiley;
439         for (j = 0; j < BUF_HEIGHT_TILES; ++j, new_y += ioy, old_y += ioy) {
440                 new_x = base_new_x;
441                 old_x = base_old_x;
442                 /* Iterate over the x tile values. */
443                 for (k = 0; k < BUF_WIDTH_TILES;
444                      ++k, new_x += iox, old_x += iox) {
445                         /* Can we get this grid block from the old buffer?. */
446                         if (old_x >= 0 && old_x < BUF_WIDTH_TILES
447                             && old_y >= 0 && old_y < BUF_HEIGHT_TILES) {
448                                 /* Copy from old buffer to new buffer. */
449                                 gdk_draw_drawable(_map_pixmap,
450                                                   _gc[COLORABLE_MARK],
451                                                   _map_pixmap,
452                                                   old_x * TILE_SIZE_PIXELS,
453                                                   old_y * TILE_SIZE_PIXELS,
454                                                   new_x * TILE_SIZE_PIXELS,
455                                                   new_y * TILE_SIZE_PIXELS,
456                                                   TILE_SIZE_PIXELS,
457                                                   TILE_SIZE_PIXELS);
458                         } else {
459                                 map_render_tile(new_base_tilex + new_x,
460                                                 new_base_tiley + new_y,
461                                                 new_x * TILE_SIZE_PIXELS,
462                                                 new_y * TILE_SIZE_PIXELS,
463                                                 FALSE);
464                         }
465                 }
466         }
467         map_render_data();
468 }
469
470 MACRO_RECALC_OFFSET();
471 MACRO_RECALC_FOCUS_BASE();
472
473 map_set_mark();
474 MACRO_QUEUE_DRAW_AREA();
475 }
476
477 /**
478  * Pan the view by the given number of units in the X and Y directions.
479  */
480 void 
481 map_pan(gint delta_unitx, gint delta_unity)
482 {
483 if (_center_mode > 0)
484         set_action_activate("autocenter_none", TRUE);
485
486 map_center_unit(_center.unitx + delta_unitx, _center.unity + delta_unity);
487 }
488
489 /**
490  * Initiate a move of the mark from the old location to the current
491  * location.  This function queues the draw area of the old mark (to force
492  * drawing of the background map), then updates the mark, then queus the
493  * draw area of the new mark.
494  */
495 void 
496 map_move_mark()
497 {
498 /* Just queue the old and new draw areas. */
499 gtk_widget_queue_draw_area(_map_widget,
500                    _mark_minx<0 ? 0 : _mark_minx,
501                    _mark_miny<0 ? 0 : _mark_miny, 
502                    _mark_width, _mark_height);
503 map_set_mark();
504 gtk_widget_queue_draw_area(_map_widget,
505                    _mark_minx<0 ? 0 : _mark_minx,
506                    _mark_miny<0 ? 0 : _mark_miny, 
507                    _mark_width, _mark_height);
508 }
509
510 gboolean
511 map_update_location_from_gps()
512 {
513 map_update_location(_pos.unitx, _pos.unity, FALSE);
514 return FALSE;
515 }
516
517 gboolean
518 map_update_location_from_center()
519 {
520 /* Force re-validation of place if user is clicking around */
521 map_loc.valid=FALSE;
522 map_update_location(_center.unitx, _center.unity, TRUE);
523 return FALSE;
524 }
525
526 /**
527  * Draw given pixbuf on map, centered on x,y
528  */
529 void
530 map_draw_pixbuf(guint unitx, guint unity, GdkPixbuf *p)
531 {
532 gint x,y;
533 x = unit2bufx(unitx);
534 y = unit2bufy(unity);
535
536 gdk_draw_pixbuf(_map_pixmap, _gc[COLORABLE_POI],
537         p, 0, 0,
538         x - gdk_pixbuf_get_width(p) / 2,
539         y - gdk_pixbuf_get_height(p) / 2,
540         -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
541 }
542
543 /**
544  * Draw an icon on given Position.
545  * 
546  * XXX: don't hardcode as home.png !
547  */
548 void
549 map_draw_position_icon(Position *pos)
550 {
551 guint x,y;
552 GdkPixbuf *p;
553
554 latlon2unit(pos->lat, pos->lon, x, y);
555 p=gdk_pixbuf_new_from_file(DATADIR "/pixmaps/mapper/home.png", NULL);
556 if (p) {
557         map_draw_pixbuf(x,y,p);
558         g_object_unref(p);
559 }
560
561 }
562
563 /**
564  * Make sure the mark is up-to-date.  This function triggers a panning of
565  * the view if the mark is appropriately close to the edge of the view.
566  */
567 void
568 map_refresh_mark()
569 {
570 guint new_center_unitx;
571 guint new_center_unity;
572
573 MACRO_RECALC_CENTER(new_center_unitx, new_center_unity);
574
575 if ((new_center_unitx - _focus.unitx) < _focus_unitwidth
576     && (new_center_unity - _focus.unity) < _focus_unitheight)
577         /* We're not changing the view - just move the mark. */
578         map_move_mark();
579 else
580         map_center_unit(new_center_unitx, new_center_unity);
581
582 /* Draw speed info */
583 if (_speed_limit_on)
584         speed_limit();
585
586 if (_center_mode>0)
587         g_idle_add((GSourceFunc)map_update_location_from_gps, NULL);
588 }
589
590 /**
591  * Render a single track line to _map_pixmap.  If either point on the line
592  * is a break (defined as unity == 0), a circle is drawn at the other point.
593  * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
594  */
595 void
596 map_render_segment(GdkGC * gc_norm, GdkGC * gc_alt,
597                    guint unitx1, guint unity1, guint unitx2, guint unity2)
598 {
599         vprintf("%s()\n", __PRETTY_FUNCTION__);
600
601         if (!unity1) {
602                 guint x2, y2;
603                 x2 = unit2bufx(unitx2);
604                 y2 = unit2bufy(unity2);
605                 /* Make sure this circle will be visible. */
606                 if ((x2 < BUF_WIDTH_PIXELS)
607                     && (y2 < BUF_HEIGHT_PIXELS))
608                         gdk_draw_arc(_map_pixmap, gc_alt, FALSE,        /* FALSE: not filled. */
609                                      x2 - _draw_width, y2 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
610                                      360 * 64);
611         } else if (!unity2) {
612                 guint x1, y1;
613                 x1 = unit2bufx(unitx1);
614                 y1 = unit2bufy(unity1);
615                 /* Make sure this circle will be visible. */
616                 if ((x1 < BUF_WIDTH_PIXELS)
617                     && ((unsigned)y1 < BUF_HEIGHT_PIXELS))
618                         gdk_draw_arc(_map_pixmap, gc_alt, FALSE,        /* FALSE: not filled. */
619                                      x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
620                                      360 * 64);
621         } else {
622                 gint x1, y1, x2, y2;
623                 x1 = unit2bufx(unitx1);
624                 y1 = unit2bufy(unity1);
625                 x2 = unit2bufx(unitx2);
626                 y2 = unit2bufy(unity2);
627                 /* Make sure this line could possibly be visible. */
628                 if (!((x1 > BUF_WIDTH_PIXELS && x2 > BUF_WIDTH_PIXELS)
629                       || (x1 < 0 && x2 < 0)
630                       || (y1 > BUF_HEIGHT_PIXELS && y2 > BUF_HEIGHT_PIXELS)
631                       || (y1 < 0 && y2 < 0)))
632                         gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2);
633         }
634
635         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
636 }
637
638 /**
639  * Render all track data onto the _map_pixmap.  Note that this does not
640  * clear the pixmap of previous track data (use map_force_redraw() for
641  * that), and also note that this method does not queue any redraws, so it
642  * is up to the caller to decide which part of the track really needs to be
643  * redrawn.
644  */
645 void 
646 map_render_path(Path * path, GdkGC ** gc)
647 {
648         Point *curr;
649         WayPoint *wcurr;
650         printf("%s()\n", __PRETTY_FUNCTION__);
651
652         /* gc is a pointer to the first GC to use (for plain points).  (gc + 1)
653          * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
654          * to the GC to use for breaks. */
655
656         /* else there is a route to draw. */
657         for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
658                 /* Draw the line from (curr - 1) to (curr). */
659                 map_render_segment(gc[0], gc[2],
660                                    curr[-1].unitx, curr[-1].unity, curr->unitx,
661                                    curr->unity);
662
663                 /* Now, check if curr is a waypoint. */
664                 if (wcurr && wcurr <= path->wtail && wcurr->point == curr) {
665                         guint x1 = unit2bufx(wcurr->point->unitx);
666                         guint y1 = unit2bufy(wcurr->point->unity);
667                         if ((x1 < BUF_WIDTH_PIXELS)
668                             && (y1 < BUF_HEIGHT_PIXELS)) {
669                                 gdk_draw_arc(_map_pixmap, gc[1], FALSE, /* FALSE: not filled. */
670                                              x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
671                                              360 * 64);
672                         }
673                         wcurr++;
674                 }
675         }
676
677         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
678 }
679
680 void 
681 map_render_paths()
682 {
683         printf("%s()\n", __PRETTY_FUNCTION__);
684
685         if ((_show_tracks & ROUTES_MASK) && _route.head != _route.tail) {
686                 map_render_path(&_route, _gc + COLORABLE_ROUTE);
687
688                 /* Now, draw the next waypoint on top of all other waypoints. */
689                 if (_next_way) {
690                         guint x1 = unit2bufx(_next_way->point->unitx);
691                         guint y1 = unit2bufy(_next_way->point->unity);
692                         if ((x1 < BUF_WIDTH_PIXELS)
693                             && (y1 < BUF_HEIGHT_PIXELS)) {
694                                 /* Draw the next waypoint as a break. */
695                                 gdk_draw_arc(_map_pixmap, _gc[COLORABLE_ROUTE_BREAK], FALSE,    /* FALSE: not filled. */
696                                              x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0,   /* start at 0 degrees. */
697                                              360 * 64);
698                         }
699                 }
700         }
701         if (_show_tracks & TRACKS_MASK)
702                 map_render_path(&_track, _gc + COLORABLE_TRACK);
703
704         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
705 }
706
707 static 
708 gboolean map_follow_move(GtkWidget * widget, GdkEventMotion * event)
709 {
710         gint xx, yy;
711         GdkModifierType state;
712         guint unitx, unity;
713         guint cx, cy;
714
715         if (event->is_hint) {
716                 gdk_window_get_pointer(event->window, &xx, &yy, &state);
717                 state = event->state;
718         } else {
719                 xx = event->x;
720                 yy = event->y;
721                 state = event->state;
722         }
723
724         unitx = x2unit((gint) (xx - before[0]));
725         unity = y2unit((gint) (yy - before[1]));
726         cx = unit2x(_center.unitx);
727         cy = unit2y(_center.unity);
728
729         map_center_unit(x2unit((gint) (cx - (xx - before[0]))),
730                         y2unit((gint) (cy - (yy - before[1]))));
731
732         before[0] = xx;
733         before[1] = yy;
734         return FALSE;
735 }
736
737 gboolean 
738 map_key_zoom_timeout()
739 {
740         printf("%s()\n", __PRETTY_FUNCTION__);
741         if (_key_zoom_new < _zoom) {
742                 /* We're currently zooming in (_zoom is decreasing). */
743                 guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
744                 if (test < MAX_ZOOM)
745                         _key_zoom_new = test;
746                 else
747                         return FALSE;
748         } else {
749                 /* We're currently zooming out (_zoom is increasing). */
750                 guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
751                 if (test < MAX_ZOOM)
752                         _key_zoom_new = test;
753                 else
754                         return FALSE;
755         }
756
757         /* We can zoom more - tell them how much they're zooming. */
758         {
759                 gchar buffer[32];
760                 snprintf(buffer, sizeof(buffer), "%s %d", _("Zoom to Level"), _key_zoom_new);
761                 MACRO_BANNER_SHOW_INFO(_window, buffer);
762         }
763         vprintf("%s(): return\n", __PRETTY_FUNCTION__);
764         return TRUE;
765 }
766
767 void 
768 map_scale_draw(GdkEventExpose *event)
769 {
770         gdk_rectangle_intersect(&event->area, &_scale_rect, &event->area);
771
772         if (event->area.width && event->area.height) {
773                 gdk_draw_rectangle(_map_widget->window,
774                                    _map_widget->style->
775                                    bg_gc[GTK_WIDGET_STATE(_map_widget)],
776                                    TRUE, _scale_rect.x, _scale_rect.y,
777                                    _scale_rect.width,
778                                    _scale_rect.height);
779                 gdk_draw_rectangle(_map_widget->window,
780                                    _map_widget->style->
781                                    fg_gc[GTK_WIDGET_STATE(_map_widget)],
782                                    FALSE, _scale_rect.x, _scale_rect.y,
783                                    _scale_rect.width,
784                                    _scale_rect.height);
785
786                 /* Now calculate and draw the distance. */
787                 {
788                         gchar buffer[16];
789                         gdouble distance;
790                         gdouble lat1, lon1, lat2, lon2;
791                         gint width;
792
793                         unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4),
794                                     _center.unity, lat1, lon1);
795                         unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4),
796                                     _center.unity, lat2, lon2);
797                         distance = calculate_distance(lat1, lon1, lat2, lon2) * UNITS_CONVERT[_units];
798
799                         if (distance < 1.f)
800                                 snprintf(buffer, sizeof(buffer),
801                                          "%0.2f %s", distance,
802                                          UNITS_TEXT[_units]);
803                         else if (distance < 10.f)
804                                 snprintf(buffer, sizeof(buffer),
805                                          "%0.1f %s", distance,
806                                          UNITS_TEXT[_units]);
807                         else
808                                 snprintf(buffer, sizeof(buffer),
809                                          "%0.f %s", distance,
810                                          UNITS_TEXT[_units]);
811
812                         pango_layout_set_text(_scale_layout, buffer, -1);
813                         pango_layout_get_pixel_size(_scale_layout, &width, NULL);
814
815                         /* Draw the layout itself. */
816                         gdk_draw_layout(_map_widget->window,
817                                         _map_widget->style->
818                                         fg_gc[GTK_WIDGET_STATE
819                                               (_map_widget)],
820                                         _scale_rect.x +
821                                         (_scale_rect.width - width) / 2,
822                                         _scale_rect.y, _scale_layout);
823
824                         /* Draw little hashes on the ends. */
825                         gdk_draw_line(_map_widget->window,
826                                       _map_widget->style->
827                                       fg_gc[GTK_WIDGET_STATE
828                                             (_map_widget)],
829                                       _scale_rect.x + 4,
830                                       _scale_rect.y +
831                                       _scale_rect.height / 2 - 4,
832                                       _scale_rect.x + 4,
833                                       _scale_rect.y +
834                                       _scale_rect.height / 2 + 4);
835                         gdk_draw_line(_map_widget->window,
836                                       _map_widget->style->
837                                       fg_gc[GTK_WIDGET_STATE
838                                             (_map_widget)],
839                                       _scale_rect.x + 4,
840                                       _scale_rect.y +
841                                       _scale_rect.height / 2,
842                                       _scale_rect.x +
843                                       (_scale_rect.width - width) / 2 -
844                                       4,
845                                       _scale_rect.y +
846                                       _scale_rect.height / 2);
847                         gdk_draw_line(_map_widget->window,
848                                       _map_widget->style->
849                                       fg_gc[GTK_WIDGET_STATE
850                                             (_map_widget)],
851                                       _scale_rect.x +
852                                       _scale_rect.width - 4,
853                                       _scale_rect.y +
854                                       _scale_rect.height / 2 - 4,
855                                       _scale_rect.x +
856                                       _scale_rect.width - 4,
857                                       _scale_rect.y +
858                                       _scale_rect.height / 2 + 4);
859                         gdk_draw_line(_map_widget->window,
860                                       _map_widget->style->
861                                       fg_gc[GTK_WIDGET_STATE
862                                             (_map_widget)],
863                                       _scale_rect.x +
864                                       _scale_rect.width - 4,
865                                       _scale_rect.y +
866                                       _scale_rect.height / 2,
867                                       _scale_rect.x +
868                                       (_scale_rect.width + width) / 2 +
869                                       4,
870                                       _scale_rect.y +
871                                       _scale_rect.height / 2);
872                 }
873         }
874 }
875
876 gboolean 
877 map_cb_expose(GtkWidget * widget, GdkEventExpose * event)
878 {
879 g_printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
880        event->area.x, event->area.y,
881        event->area.width, event->area.height);
882
883 gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
884                   _gc[COLORABLE_MARK],
885                   _map_pixmap,
886                   event->area.x + _offsetx, event->area.y + _offsety,
887                   event->area.x, event->area.y,
888                   event->area.width, event->area.height);
889 map_draw_mark();
890
891 /* Draw scale, if necessary. */
892 if (_show_scale)
893         map_scale_draw(event);
894
895 return TRUE;
896 }
897
898 int 
899 map_zoom(gint zdir)
900 {
901 gchar buffer[80];
902 gint nzoom;
903
904 nzoom = _zoom + zdir;
905
906 if ((nzoom > 0) && (nzoom < MAX_ZOOM - 1)) {
907         snprintf(buffer, sizeof(buffer), "%s %d", _("Zoom to Level"), nzoom);
908         MACRO_BANNER_SHOW_INFO(_window, buffer);
909         map_set_zoom(nzoom);
910 }
911
912 return nzoom;
913 }
914
915 static gboolean
916 map_autozoomer()
917 {
918 static gfloat z=5.0;
919 static gint last=5;
920 gint iz;
921
922 if (zoom_timeout_sid==0)
923         return FALSE;
924
925 z=(z+_gps.speed+1)/5;
926 if (z>5) z=5.0; else if (z<1) z=1.0;
927 iz=(gint)roundf(z);
928 #ifdef DEBUG
929 g_printf("Setting autozoom to: %f %d S:%f\n", z, iz, _gps.speed);
930 #endif
931 if (iz>last) 
932         iz=last+1; 
933 else if (iz<last) 
934         iz=last-1;
935 last=iz;
936 map_set_zoom(iz);
937
938 return TRUE;
939 }
940
941 void
942 map_set_autozoom(gboolean az)
943 {
944 if (az==TRUE) {
945         MACRO_BANNER_SHOW_INFO(_window, "Autozoom enabled");
946         zoom_timeout_sid=g_timeout_add(_gps.speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
947         return;
948 } else {
949         if (zoom_timeout_sid) {
950                 g_source_remove(zoom_timeout_sid);
951                 zoom_timeout_sid=0;
952                 MACRO_BANNER_SHOW_INFO(_window, "Autozoom disabled");
953         }
954         return;
955 }
956
957 }
958
959 static void 
960 map_draw_route(gint x, gint y)
961 {
962 cmenu_route_add_way(x, y);
963 }
964
965 static void 
966 map_draw_track(gint x, gint y)
967 {
968 _pos.unitx = x2unit((gint) (x + 0.5));
969 _pos.unity = y2unit((gint) (y + 0.5));
970 unit2latlon(_pos.unitx, _pos.unity, _gps.lat, _gps.lon);
971 _gps.speed = 20.f;
972 integerize_data();
973 track_add(time(NULL), FALSE);
974 map_refresh_mark();
975 }
976
977 static void
978 map_set_place_information(osm_way *s, osm_place *mp, osm_place *sp)
979 {
980 gchar buffer[256];
981
982 if (!s && !mp && !sp) {
983         snprintf(buffer, sizeof(buffer), "Unknown location");
984 } else {
985         /* oh, fun */
986         snprintf(buffer, sizeof(buffer), "On %s%s%s%s%s%s%s%s%s", 
987         s ? s->name ? s->name : _("unknown") : "?",
988         s ? s->ref ? ", " : "" : "",
989         s ? s->ref ? s->ref : "" : "",
990         s ? s->int_ref ? ", " : "" : "",
991         s ? s->int_ref ? s->int_ref : "" : "",
992         (sp && sp->name) ? " in " : "",
993         (sp && sp->name) ? sp->name : "",
994         (mp && mp->name) ? " in " : "",
995         (mp && mp->name) ? mp->name : "");
996 }
997 gtk_label_set_label(GTK_LABEL(info_banner.location), buffer);
998 }
999
1000 static void
1001 map_update_destination(gdouble lat, gdouble lon)
1002 {
1003 if (_dest.valid) {
1004         gchar buffer[32];
1005         gdouble dt=calculate_distance(lat, lon, _dest.lat, _dest.lon);
1006         gdouble dh=calculate_course(lat, lon, _dest.lat, _dest.lon);
1007         snprintf(buffer, sizeof(buffer), "%.02f %s (%0.02f)", dt * UNITS_CONVERT[_units], UNITS_TEXT[_units], dh);
1008         gtk_label_set_label(GTK_LABEL(info_banner.distance), buffer);
1009 } else {
1010         gtk_label_set_label(GTK_LABEL(info_banner.distance), "");
1011 }
1012 }
1013
1014 static void 
1015 map_update_location(gint x, gint y, gboolean force)
1016 {
1017 gint ilat, ilon;
1018 gdouble lat,lon;
1019 static gboolean inp=FALSE;
1020
1021 /* We run the gtk mainloop in progress callback so we can be called again, we don't like that */
1022 if (inp==TRUE)
1023         return;
1024 inp=TRUE;
1025
1026 unit2latlon(x, y, lat, lon);
1027 ilat=lat2mp_int(lat);
1028 ilon=lon2mp_int(lon);
1029
1030 if (_gps.fix>1 && !force)
1031         osm_set_way_range_from_speed(_gps.speed);
1032 else
1033         osm_set_way_range(OSM_RANGE_WAY/4);
1034
1035 osm_progress_set_widget(_db, _progress_item);
1036 _map_location_known=osm_get_location_data(ilat, ilon, &map_loc);
1037 _map_location_dist=map_loc.street ? map_loc.street->dist : 900000.0;
1038 if (map_loc.valid)
1039         map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
1040 else
1041         map_set_place_information(NULL, NULL, NULL);
1042 osm_progress_set_widget(_db, NULL);
1043
1044 map_update_destination(lat, lon);
1045
1046 inp=FALSE;
1047 }
1048
1049 gboolean 
1050 map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
1051 {
1052 if (event->direction == GDK_SCROLL_UP) {
1053         map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1054         map_set_zoom(_zoom - 1);
1055 } else if (event->direction == GDK_SCROLL_DOWN) {
1056         map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1057         map_set_zoom(_zoom + 1);
1058 }
1059 return FALSE;
1060 }
1061
1062 gboolean 
1063 map_cb_button_press(GtkWidget * widget, GdkEventButton * event)
1064 {
1065         printf("%s()\n", __PRETTY_FUNCTION__);
1066
1067         _cmenu_position_x = event->x + 0.5;
1068         _cmenu_position_y = event->y + 0.5;
1069
1070         switch (event->button) {
1071         case 1:
1072                 if (event->type == GDK_2BUTTON_PRESS) {
1073                         map_set_zoom(_zoom - 1);
1074                         return FALSE;
1075                 }
1076                 if (event->type == GDK_3BUTTON_PRESS)
1077                         return FALSE;
1078                 break;
1079         case 2:
1080                 press[0] = event->x;
1081                 press[1] = event->y;
1082                 before[0] = press[0];
1083                 before[1] = press[1];
1084
1085                 _id = g_signal_connect(G_OBJECT(_map_widget),
1086                                      "motion_notify_event",
1087                                      G_CALLBACK(map_follow_move), NULL);
1088                 break;
1089         case 3:
1090 #ifndef WITH_HILDON
1091                 gtk_menu_popup(GTK_MENU(_menu_map), NULL, NULL, NULL, NULL,
1092                                event->button, gtk_get_current_event_time());
1093 #endif
1094                 break;
1095         }
1096
1097         /* Return FALSE to allow context menu to work. */
1098         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1099         return FALSE;
1100 }
1101
1102 gboolean 
1103 map_cb_button_release(GtkWidget * widget, GdkEventButton * event)
1104 {
1105 switch (event->button) {
1106         case 1:
1107                 if (_center_mode > 0)
1108                         set_action_activate("autocenter_none", TRUE);
1109
1110                 switch (map_mode) {
1111                 case MAP_MODE_DRAW_TRACK:
1112                         map_draw_track(event->x, event->y);
1113                 break;
1114                 case MAP_MODE_DRAW_ROUTE:
1115                         map_draw_route(event->x, event->y);
1116                 break;
1117                 case MAP_MODE_SET_ROUTE_FROM:
1118                 case MAP_MODE_SET_ROUTE_POINT:
1119                 case MAP_MODE_SET_ROUTE_TO:
1120                 break;
1121                 }
1122
1123 #if 0
1124                 ux = x2unit((gint) (event->x + 0.5));
1125                 uy = y2unit((gint) (event->y + 0.5));
1126                 map_update_location(ux, uy);
1127 #else
1128                 g_idle_add((GSourceFunc)map_update_location_from_center, NULL);
1129 #endif
1130
1131                 map_center_unit(x2unit((gint) (event->x + 0.5)),
1132                                 y2unit((gint) (event->y + 0.5)));
1133
1134                 break;
1135         case 2:
1136                 release[0] = event->x;
1137                 release[1] = event->y;
1138
1139                 g_signal_handler_disconnect(G_OBJECT(_map_widget), _id);
1140
1141                 press[0] = 0;
1142                 press[1] = 0;
1143                 release[0] = 0;
1144                 release[1] = 0;
1145                 before[0] = 0;
1146                 before[1] = 0;
1147                 break;
1148         case 3:
1149                 break;
1150 }
1151
1152 /* Return FALSE to avoid context menu (if it hasn't popped up already). */
1153 return FALSE;
1154 }