2 * This file is part of mapper
4 * Copyright (C) 2007 Kaj-Michael Lang
5 * Copyright (C) 2006-2007 John Costigan.
7 * POI and GPS-Info code originally written by Cezary Jackiewicz.
9 * Default map data provided by http://www.openstreetmap.org/
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
36 #include <glib/gstdio.h>
42 #include "hildon-mapper.h"
53 #include "mapper-types.h"
54 #include "ui-common.h"
60 #include "map-download.h"
61 #include "announcements.h"
62 #include "gtkcompass.h"
64 #include "image-cache.h"
66 #define DEBUG_MAP_TIME 1
68 #define MAP_THUMB_MARGIN_X (100)
69 #define MAP_THUMB_MARGIN_Y (75)
72 #define BUF_WIDTH_TILES (4)
73 #define BUF_HEIGHT_TILES (3)
74 #define BUF_WIDTH_PIXELS (1024)
75 #define BUF_HEIGHT_PIXELS (768)
77 static guint buf_width_tiles=BUF_WIDTH_TILES;
78 static guint buf_height_tiles=BUF_HEIGHT_TILES;
79 static guint buf_width_pixels=BUF_WIDTH_PIXELS;
80 static guint buf_height_pixels=BUF_HEIGHT_PIXELS;
84 #include <gtk/gtkgl.h>
85 GdkGLConfig* map_gl_config=NULL;
87 gboolean map_gl=FALSE;
93 /** The backing pixmap of map_widget. */
94 static GdkPixmap *map_pixmap;
96 /* Tile cache, this might need some adjustment */
97 #define MAP_CACHE_DEFAULT (64)
98 static ImageCache *map_ic;
100 /** The "base tile" is the upper-left tile in the pixmap. */
101 guint _base_tilex = -5;
102 guint _base_tiley = -5;
104 static guint map_max_zoom=MAX_ZOOM;
105 guint _zoom = 3; /* zoom level, from 0 to MAX_ZOOM. */
107 Point _min_center = { -1, -1 };
108 Point _max_center = { -1, -1 };
109 Point _focus = { -1, -1 };
110 /* current center location, X. */
111 Point _center = { -1, -1 };
113 CenterMode _center_mode = CENTER_LEAD;
116 static guint press[2] = { 0, 0 };
117 static guint release[2] = { 0, 0 };
118 static guint before[2] = { 0, 0 };
119 static guint map_drag_id = 0;
120 static gboolean map_dragged;
122 /** VARIABLES FOR ACCESSING THE LOCATION/BOUNDS OF THE CURRENT MARK. */
127 static gint mark_minx;
128 static gint mark_miny;
129 static gint mark_width;
130 static gint mark_height;
132 static GdkRectangle scale_rect;
133 static PangoContext *scale_context;
134 static PangoFontDescription *scale_font;
135 static PangoLayout *scale_layout;
137 static GdkGC *speed_gc1;
138 static GdkGC *speed_gc2;
139 static PangoContext *speed_context;
140 static PangoLayout *speed_layout;
141 static PangoFontDescription *speed_fontdesc;
143 static gint zoom_timeout_sid=0;
144 static gint map_mode=0;
145 static gboolean map_data_needs_refresh=FALSE;
147 osm_location map_loc = {NULL, NULL, NULL, FALSE, FALSE, 0, 0, 0.0, 0.0, 0 };
149 static GTimer *map_timer;
151 /* Tile max age, 1 week */
152 #define TILE_MAX_AGE (604800)
154 #define KM10KNOTS (5.39956803)
156 static void map_update_location(gdouble lat, gdouble lon, gboolean force);
157 static void map_speed_draw(void);
158 static gboolean map_cb_after_realize(GtkWidget *map_widget, gpointer data);
159 static gboolean map_cb_configure(GtkWidget *widget, GdkEventConfigure *event);
160 static gboolean map_cb_expose(GtkWidget * widget, GdkEventExpose * event);
161 static gboolean map_cb_button_press(GtkWidget * widget, GdkEventButton * event);
162 static gboolean map_cb_button_release(GtkWidget * widget, GdkEventButton * event);
163 static gboolean map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event);
165 /******************************************************************************/
170 GtkWidget *map_widget;
172 map_widget=gtk_drawing_area_new();
173 map_timer=g_timer_new();
175 map_ic=image_cache_new(MAP_CACHE_DEFAULT);
177 gtk_widget_set_extension_events(GTK_WIDGET(map_widget), GDK_EXTENSION_EVENTS_ALL);
180 map_gl_config=gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
182 g_print("OpenGL version: %s\n", glGetString (GL_VERSION));
183 g_print("OpenGL vendor: %s\n", glGetString (GL_VENDOR));
184 g_print("OpenGL renderer: %s\n", glGetString (GL_RENDERER));
185 gtk_widget_set_gl_capability(map_widget, map_gl_config, NULL, TRUE, GDK_GL_RGBA_TYPE);
190 g_signal_connect_after(G_OBJECT(map_widget), "realize", G_CALLBACK(map_cb_after_realize), NULL);
191 g_signal_connect(G_OBJECT(map_widget), "configure_event", G_CALLBACK(map_cb_configure), NULL);
197 map_set_cache_size(guint cache_size)
199 image_cache_set_size(map_ic, cache_size);
203 map_cb_after_realize(GtkWidget *map_widget, gpointer data)
207 g_debug("MAP: after_realize");
209 scale_context=gtk_widget_get_pango_context(map_widget);
210 scale_layout=pango_layout_new(scale_context);
211 scale_font=pango_font_description_new();
212 pango_font_description_set_size(scale_font, 12 * PANGO_SCALE);
213 pango_layout_set_font_description(scale_layout, scale_font);
215 /* Speed limit, over limit color */
216 speed_gc1=gdk_gc_new(map_widget->window);
220 gdk_gc_set_rgb_fg_color(speed_gc1, &color);
222 /* Speed limit, under limit color */
223 speed_gc2=gdk_gc_new(map_widget->window);
227 gdk_gc_set_rgb_fg_color(speed_gc2, &color);
229 speed_context=gtk_widget_get_pango_context(map_widget);
230 speed_layout=pango_layout_new(speed_context);
231 speed_fontdesc=pango_font_description_new();
232 pango_font_description_set_size(speed_fontdesc, 48 * PANGO_SCALE);
233 pango_layout_set_font_description(speed_layout, speed_fontdesc);
234 pango_layout_set_alignment(speed_layout, PANGO_ALIGN_CENTER);
237 g_signal_connect(G_OBJECT(map_widget), "expose_event", G_CALLBACK(map_cb_expose), NULL);
238 g_signal_connect(G_OBJECT(map_widget), "button_press_event", G_CALLBACK(map_cb_button_press), NULL);
239 g_signal_connect(G_OBJECT(map_widget), "button_release_event",G_CALLBACK(map_cb_button_release), NULL);
240 g_signal_connect(G_OBJECT(map_widget), "scroll_event", G_CALLBACK(map_cb_scroll_event), NULL);
242 gtk_widget_add_events(map_widget, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
243 | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
245 map_poi_init(map_widget);
251 map_cb_configure(GtkWidget *widget, GdkEventConfigure *event)
255 tw=TILE_SIZE_PIXELS*((widget->allocation.width/TILE_SIZE_PIXELS)+2);
256 th=TILE_SIZE_PIXELS*((widget->allocation.height/TILE_SIZE_PIXELS)+2);
258 if (map_pixmap==NULL) {
259 map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
261 if (tw>buf_width_pixels || th>buf_height_pixels) {
262 g_object_unref(map_pixmap);
263 map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
264 } else if (tw<buf_width_pixels-(TILE_SIZE_PIXELS*2) || th<buf_height_pixels-(TILE_SIZE_PIXELS*2)) {
265 g_object_unref(map_pixmap);
266 map_pixmap=gdk_pixmap_new(widget->window, tw, th, -1);
269 g_assert(map_pixmap);
272 buf_height_pixels=th;
273 buf_width_tiles=buf_width_pixels/TILE_SIZE_PIXELS;
274 buf_height_tiles=buf_height_pixels/TILE_SIZE_PIXELS;
276 _screen_width_pixels = widget->allocation.width;
277 _screen_height_pixels = widget->allocation.height;
278 _screen_grids_halfwidth = pixel2grid(_screen_width_pixels) / 2;
279 _screen_grids_halfheight = pixel2grid(_screen_height_pixels) / 2;
281 /* Set scale_rect. */
282 scale_rect.x = (_screen_width_pixels - SCALE_WIDTH) / 2;
283 scale_rect.width = SCALE_WIDTH;
285 MACRO_RECALC_FOCUS_BASE(_center_ratio);
286 MACRO_RECALC_FOCUS_SIZE(_center_ratio);
288 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
289 _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
290 _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
291 _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
298 * Draw the current mark (representing the current GPS location) onto
299 * _map_widget. This method does not queue the draw area.
302 map_draw_mark(Gps *gps)
304 gdk_draw_arc(_map_widget->window, gps->io.conn == RCVR_FIXED ? _gc[COLORABLE_MARK] : _gc[COLORABLE_MARK_OLD], FALSE,
305 mark_x1 - _draw_width, mark_y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
306 gdk_draw_line(_map_widget->window, gps->io.conn == RCVR_FIXED ? (_show_velvec ? _gc[COLORABLE_MARK_VELOCITY] : _gc[COLORABLE_MARK]) : _gc[COLORABLE_MARK_OLD],
307 mark_x1, mark_y1, mark_x2, mark_y2);
311 * "Set" the mark, which translates the current GPS position into on-screen
312 * units in preparation for drawing the mark with map_draw_mark().
315 map_set_mark(GpsData *gps)
317 mark_x1 = unit2x(gps->unitx);
318 mark_y1 = unit2y(gps->unity);
319 mark_x2 = mark_x1 + (_show_velvec ? gps->vel_offsetx : 0);
320 mark_y2 = mark_y1 + (_show_velvec ? gps->vel_offsety : 0);
321 mark_minx = MIN(mark_x1, mark_x2) - (2 * _draw_width);
322 mark_miny = MIN(mark_y1, mark_y2) - (2 * _draw_width);
323 mark_width = abs(mark_x1 - mark_x2) + (4 * _draw_width);
324 mark_height = abs(mark_y1 - mark_y2) + (4 * _draw_width);
328 * Do an in-place scaling of a pixbuf's pixels at the given ratio from the
329 * given source location. It would have been nice if gdk_pixbuf provided
330 * this method, but I guess it's not general-purpose enough.
333 map_pixbuf_scale_inplace(GdkPixbuf *pixbuf, guint ratio_p2, guint src_x, guint src_y)
335 guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS;
336 guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
337 guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
338 guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
340 /* Sweep through the entire dest area, copying as necessary, but
341 * DO NOT OVERWRITE THE SOURCE AREA. We'll copy it afterward. */
343 guint src_dim = dest_dim >> ratio_p2;
344 guint src_endx = src_x - dest_x + src_dim;
347 for (y = dest_dim - 1; y >= 0; y--) {
348 guint src_offset_y, dest_offset_y;
350 src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
351 dest_offset_y = (dest_y + y) * rowstride;
354 if ((unsigned)(dest_y + y - src_y) < src_dim && (unsigned)(dest_x + x - src_x) < src_dim)
357 for (; x >= 0; x--) {
358 guint src_offset, dest_offset, i;
360 src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
361 dest_offset = dest_offset_y + (dest_x + x) * n_channels;
363 pixels[dest_offset] = pixels[src_offset];
364 for (i = n_channels - 1; i; i--)
365 pixels[dest_offset + i] = pixels[src_offset + i];
367 if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
372 /* Reuse src_dim and src_endx to store new src_x and src_y. */
373 src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
374 src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
380 while ((dest_dim >>= ratio_p2) > 1);
384 * Trim pixbufs that are bigger than tiles. (Those pixbufs result, when
385 * captions should be cut off.)
388 pixbuf_trim(GdkPixbuf * pixbuf)
390 GdkPixbuf *mpixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf),
391 8, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
394 gdk_pixbuf_copy_area(pixbuf,
395 (gdk_pixbuf_get_width(pixbuf) - TILE_SIZE_PIXELS) / 2,
396 (gdk_pixbuf_get_height(pixbuf) - TILE_SIZE_PIXELS) / 2,
398 TILE_SIZE_PIXELS, mpixbuf, 0, 0);
400 g_object_unref(pixbuf);
411 map_tile_load(guint tilex, guint tiley, gint zoff, gboolean download)
414 GError *error = NULL;
415 gchar buffer[BUFFER_SIZE];
416 gchar key[BUFFER_SIZE];
420 g_snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg", _curr_repo->cache_dir, _zoom + zoff, (tilex >> zoff), (tiley >> zoff));
421 g_snprintf(key, sizeof(key), "%s/%u/%u/%u", _curr_repo->cache_dir, _zoom + zoff, (tilex >> zoff), (tiley >> zoff));
423 pixbuf=image_cache_get(map_ic, key, buffer);
427 if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
429 map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
434 g_object_ref(pixbuf);
437 /* Check if we need to trim. */
438 if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
439 pixbuf=pixbuf_trim(pixbuf);
442 /* Check tile age, if file date is ower a week old, redownload if autodownload enabled */
443 se=stat(buffer, &tstat);
447 if (t-tstat.st_mtime>TILE_MAX_AGE) {
448 if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
450 g_debug("Tile: %s is old, re-downloading\n", buffer);
451 map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
452 image_cache_invalidate(map_ic, key);
462 map_render_tile(guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
464 GdkPixbuf *pixbuf=NULL;
467 if (destx > buf_width_pixels || desty > buf_height_pixels)
470 if (tilex > _world_size_tiles || tiley > _world_size_tiles)
473 g_debug("MAP RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, _world_size_tiles, destx, desty, buf_width_tiles, buf_height_tiles);
475 /* The tile is possible. */
476 for (zoff = (_curr_repo->double_size ? 1 : 0); !pixbuf && (_zoom + zoff) <= map_max_zoom && zoff <= TILE_SIZE_P2; zoff += 1) {
477 pixbuf=map_tile_load(tilex, tiley, zoff, !fast_fail);
482 /* Check if we need to blit. */
484 map_pixbuf_scale_inplace(pixbuf, zoff,
485 (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff),
486 (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff));
487 image_cache_invalidate_by_image(map_ic, pixbuf);
493 gdk_draw_pixbuf(map_pixmap, _gc[COLORABLE_MARK],
494 pixbuf, 0, 0, destx, desty,
495 TILE_SIZE_PIXELS, TILE_SIZE_PIXELS, GDK_RGB_DITHER_NONE, 0, 0);
497 g_object_unref(pixbuf);
500 gdk_draw_rectangle(map_pixmap, _map_widget->style->black_gc, TRUE, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS);
505 map_render_data(void)
507 /* Don't update if dragging, too much lag */
512 map_render_all_pois(buf_width_pixels, buf_height_pixels);
515 map_draw_position_icon(_home, "home");
518 map_draw_position_icon(_dest, "destination");
525 * Force a redraw of the entire map_pixmap, including fetching the
526 * background maps from disk and redrawing the tracks on top of them.
529 map_force_redraw(void)
535 g_timer_start(map_timer);
537 for (new_y = 0; new_y < buf_height_tiles; ++new_y)
538 for (new_x = 0; new_x < buf_width_tiles; ++new_x) {
539 map_render_tile(_base_tilex + new_x, _base_tiley + new_y,
540 new_x * TILE_SIZE_PIXELS, new_y * TILE_SIZE_PIXELS, FALSE);
543 g_timer_stop(map_timer);
544 g_debug("Full redraw: %f sec\n", g_timer_elapsed(map_timer, &tms));
548 MACRO_QUEUE_DRAW_AREA();
558 * Set the current zoom level. If the given zoom level is the same as the
559 * current zoom level, or if the new zoom is invalid
560 * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing.
563 map_set_zoom(guint new_zoom)
565 /* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if
566 * condition also checks for new_zoom >= MIN_ZOOM. */
567 if (new_zoom > (map_max_zoom - 1))
569 if (new_zoom == _zoom)
572 _zoom = new_zoom / _curr_repo->view_zoom_steps * _curr_repo->view_zoom_steps;
573 _world_size_tiles = unit2tile(WORLD_SIZE_UNITS);
575 /* If we're leading, update the center to reflect new zoom level. */
576 MACRO_RECALC_CENTER(_gps->data, _center.unitx, _center.unity);
578 /* Update center bounds to reflect new zoom level. */
579 _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth));
580 _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight));
581 _max_center.unitx = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfwidth) - 1;
582 _max_center.unity = WORLD_SIZE_UNITS - grid2unit(_screen_grids_halfheight) - 1;
584 BOUND(_center.unitx, _min_center.unitx, _max_center.unitx);
585 BOUND(_center.unity, _min_center.unity, _max_center.unity);
587 _base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) _center.unitx)) - (gint) _screen_grids_halfwidth);
588 _base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity)) - _screen_grids_halfheight);
590 /* New zoom level, so we can't reuse the old buffer's pixels. */
591 /* Update state variables. */
592 MACRO_RECALC_OFFSET();
593 MACRO_RECALC_FOCUS_BASE(_center_ratio);
594 MACRO_RECALC_FOCUS_SIZE(_center_ratio);
596 map_set_mark(&_gps->data);
602 * Center the view on the given unitx/unity.
605 map_center_unit(guint new_center_unitx, guint new_center_unity)
607 gint new_base_tilex, new_base_tiley;
609 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
611 /* Assure that _center.unitx/y are bounded. */
612 BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx);
613 BOUND(new_center_unity, _min_center.unity, _max_center.unity);
615 _center.unitx = new_center_unitx;
616 _center.unity = new_center_unity;
618 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) _center.unitx)) - (gint)_screen_grids_halfwidth);
619 new_base_tiley = grid2tile(pixel2grid(unit2pixel(_center.unity)) - _screen_grids_halfheight);
621 /* Same zoom level, so it's likely that we can reuse some of the old
622 * buffer's pixels. */
624 if (new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) {
625 /* If copying from old parts to new parts, we need to make sure we
626 * don't overwrite the old parts when copying, so set up new_x,
627 * new_y, old_x, old_y, iox, and ioy with that in mind. */
628 if (new_base_tiley < _base_tiley) {
629 /* New is lower than old - start at bottom and go up. */
630 new_y = buf_height_tiles - 1;
633 /* New is higher than old - start at top and go down. */
637 if (new_base_tilex < _base_tilex) {
638 /* New is righter than old - start at right and go left. */
639 base_new_x = buf_width_tiles - 1;
642 /* New is lefter than old - start at left and go right. */
647 /* Iterate over the y tile values. */
648 old_y = new_y + new_base_tiley - _base_tiley;
649 base_old_x = base_new_x + new_base_tilex - _base_tilex;
650 _base_tilex = new_base_tilex;
651 _base_tiley = new_base_tiley;
653 for (j = 0; j < buf_height_tiles; ++j, new_y += ioy, old_y += ioy) {
656 /* Iterate over the x tile values. */
657 for (k = 0; k < buf_width_tiles; ++k, new_x += iox, old_x += iox) {
658 /* Can we get this grid block from the old buffer?. */
659 if (old_x >= 0 && old_x < buf_width_tiles && old_y >= 0 && old_y < buf_height_tiles) {
660 /* Copy from old buffer to new buffer. */
661 gdk_draw_drawable(map_pixmap,
664 old_x * TILE_SIZE_PIXELS,
665 old_y * TILE_SIZE_PIXELS,
666 new_x * TILE_SIZE_PIXELS,
667 new_y * TILE_SIZE_PIXELS,
671 map_render_tile(new_base_tilex + new_x,
672 new_base_tiley + new_y,
673 new_x * TILE_SIZE_PIXELS,
674 new_y * TILE_SIZE_PIXELS,
675 map_drag_id!=0 ? TRUE : FALSE);
682 MACRO_RECALC_OFFSET();
683 MACRO_RECALC_FOCUS_BASE(_center_ratio);
685 map_set_mark(&_gps->data);
686 MACRO_QUEUE_DRAW_AREA();
690 * Pan the view by the given number of units in the X and Y directions.
693 map_pan(gint delta_unitx, gint delta_unity)
695 if (_center_mode > 0)
696 set_action_activate("autocenter_none", TRUE);
698 map_center_unit(_center.unitx + delta_unitx, _center.unity + delta_unity);
702 * Helper to center map on given lat/lon
705 map_center_latlon(gdouble lat, gdouble lon)
709 latlon2unit(lat, lon, unitx, unity);
710 map_center_unit(unitx, unity);
714 * Helper to goto given point and update location
718 map_goto_position(Position *pos)
720 if (pos->valid==FALSE)
723 _center_mode=CENTER_MANUAL;
724 map_center_latlon(pos->lat, pos->lon);
725 map_set_autozoom(FALSE, 0);
726 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
731 * Initiate a move of the mark from the old location to the current
732 * location. This function queues the draw area of the old mark (to force
733 * drawing of the background map), then updates the mark, then queus the
734 * draw area of the new mark.
739 /* Just queue the old and new draw areas. */
740 gtk_widget_queue_draw_area(_map_widget,
741 mark_minx<0 ? 0 : mark_minx,
742 mark_miny<0 ? 0 : mark_miny,
743 mark_width, mark_height);
744 map_set_mark(&_gps->data);
745 gtk_widget_queue_draw_area(_map_widget,
746 mark_minx<0 ? 0 : mark_minx,
747 mark_miny<0 ? 0 : mark_miny,
748 mark_width, mark_height);
752 map_update_location_from_gps(Gps *gps)
755 map_update_location(gps->data.lat, gps->data.lon, FALSE);
760 map_update_location_from_center(void)
763 /* Force re-validation of place if user is clicking around */
765 /* XXX: hmm, not the right place for this */
767 if (_gps->fix==FIX_NOFIX) {
768 _gps->data.unitx=_center.unitx;
769 _gps->data.unity=_center.unity;
770 unit2latlon(_gps->data.unitx, _gps->data.unity, _gps->data.lat, _gps->data.lon);
773 unit2latlon(_center.unitx, _center.unity, lat, lon);
774 map_update_location(lat, lon, TRUE);
779 map_draw_pixbuf_on_buffer(gint x, gint y, GdkPixbuf *p)
781 if ((x < 0) || (y < 0))
784 if ((x > buf_width_pixels) || (y > buf_height_pixels))
787 gdk_draw_pixbuf(map_pixmap, _gc[COLORABLE_POI],
789 x - gdk_pixbuf_get_width(p) / 2,
790 y - gdk_pixbuf_get_height(p) / 2,
791 -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
795 * Draw given pixbuf on map, centered on x,y
798 map_draw_pixbuf(guint unitx, guint unity, GdkPixbuf *p)
804 map_draw_pixbuf_on_buffer(x, y, p);
808 * Draw an icon at given Position.
812 map_draw_position_icon(Position *pos, const gchar *icon)
819 latlon2unit(pos->lat, pos->lon, ux, uy);
823 if ((x < 0) || (y < 0))
826 if ((x > buf_width_pixels) || (y > buf_height_pixels))
829 g_snprintf(buffer, sizeof(buffer), "%s/pixmaps/mapper/%s.png", DATADIR, icon);
830 p=gdk_pixbuf_new_from_file(buffer, NULL);
834 map_draw_pixbuf_on_buffer(x, y, p);
839 * Make sure the mark is up-to-date. This function triggers a panning of
840 * the view if the mark is appropriately close to the edge of the view.
843 map_refresh_mark(void)
845 guint new_center_unitx;
846 guint new_center_unity;
848 MACRO_RECALC_CENTER(_gps->data, new_center_unitx, new_center_unity);
850 if ((new_center_unitx - _focus.unitx) < _focus_unitwidth && (new_center_unity - _focus.unity) < _focus_unitheight)
851 /* We're not changing the view - just move the mark. */
854 map_center_unit(new_center_unitx, new_center_unity);
856 /* Draw speed info */
861 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_gps, _gps, NULL);
865 * Render a single track line to map_pixmap. If either point on the line
866 * is a break (defined as unity == 0), a circle is drawn at the other point.
867 * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK.
870 map_render_segment(GdkGC *gc_norm, GdkGC *gc_alt, guint unitx1, guint unity1, guint unitx2, guint unity2)
875 x2 = unit2bufx(unitx2);
876 y2 = unit2bufy(unity2);
877 /* Make sure this circle will be visible. */
878 if ((x2 < buf_width_pixels) && (y2 < buf_height_pixels))
879 gdk_draw_arc(map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */
880 x2 - _draw_width, y2 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
882 } else if (!unity2) {
885 x1 = unit2bufx(unitx1);
886 y1 = unit2bufy(unity1);
887 /* Make sure this circle will be visible. */
888 if ((x1 < buf_width_pixels) && ((unsigned)y1 < buf_height_pixels))
889 gdk_draw_arc(map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */
890 x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, /* start at 0 degrees. */
895 x1 = unit2bufx(unitx1);
896 y1 = unit2bufy(unity1);
897 x2 = unit2bufx(unitx2);
898 y2 = unit2bufy(unity2);
899 /* Make sure this line could possibly be visible. */
900 if (!((x1 > buf_width_pixels && x2 > buf_width_pixels)
901 || (x1 < 0 && x2 < 0)
902 || (y1 > buf_height_pixels && y2 > buf_height_pixels)
903 || (y1 < 0 && y2 < 0)))
904 gdk_draw_line(map_pixmap, gc_norm, x1, y1, x2, y2);
909 map_render_waypoint(guint x1, guint y1, GdkGC *gc)
911 if ((x1 > buf_width_pixels) || (y1 > buf_height_pixels))
913 gdk_draw_arc(map_pixmap, gc, FALSE, x1 - _draw_width, y1 - _draw_width, 2 * _draw_width, 2 * _draw_width, 0, 360 * 64);
917 * Render all track data onto the map_pixmap. Note that this does not
918 * clear the pixmap of previous track data (use map_force_redraw() for
919 * that), and also note that this method does not queue any redraws, so it
920 * is up to the caller to decide which part of the track really needs to be
924 map_render_path(Path *path, GdkGC **gc)
929 g_return_if_fail(path);
930 if (path->head==path->tail)
933 /* gc is a pointer to the first GC to use (for plain points). (gc + 1)
934 * is a pointer to the GC to use for waypoints, and (gc + 2) is a pointer
935 * to the GC to use for breaks. */
937 /* else there is a route to draw. */
938 for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
939 /* Draw the line from (curr - 1) to (curr). */
940 map_render_segment(gc[0], gc[2], curr[-1].unitx, curr[-1].unity, curr->unitx, curr->unity);
942 /* Now, check if curr is a waypoint. */
943 if (wcurr && wcurr <= path->wtail && wcurr->point == curr) {
944 guint x1 = unit2bufx(wcurr->point->unitx);
945 guint y1 = unit2bufy(wcurr->point->unity);
947 map_render_waypoint(x1, y1, _gc[COLORABLE_TRACK_BREAK]);
954 map_render_next_waypoint(Path *path)
958 g_return_if_fail(path);
962 g_return_if_fail(path->next_way->point);
964 x1=unit2bufx(path->next_way->point->unitx);
965 y1=unit2bufy(path->next_way->point->unity);
967 if ((x1 < buf_width_pixels) && (y1 < buf_height_pixels)) {
968 /* Draw the next waypoint as a break. */
969 gdk_draw_arc(map_pixmap, _gc[COLORABLE_ROUTE_BREAK], FALSE,
970 x1 - _draw_width, y1 - _draw_width,
971 4 * _draw_width, 4 * _draw_width, 0, 360 * 64);
976 map_render_paths(void)
978 if (_show_tracks & ROUTES_MASK) {
979 map_render_path(_route, _gc + COLORABLE_ROUTE);
981 /* Now, draw the next waypoint on top of all other waypoints. */
982 map_render_next_waypoint(_route);
984 if (_show_tracks & TRACKS_MASK)
985 map_render_path(_track, _gc + COLORABLE_TRACK);
993 map_follow_move_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
995 GdkModifierType state;
997 guint unitx, unity, nunitx, nunity;
1000 if (!(event->state & GDK_BUTTON1_MASK)) {
1005 if (event->is_hint) {
1006 gdk_window_get_pointer(event->window, &xx, &yy, &state);
1007 state = event->state;
1011 state = event->state;
1014 unitx = x2unit((gint) (xx - before[0]));
1015 unity = y2unit((gint) (yy - before[1]));
1016 cx = unit2x(_center.unitx);
1017 cy = unit2y(_center.unity);
1019 nunitx=x2unit((gint) (cx - (xx - before[0])));
1020 nunity=y2unit((gint) (cy - (yy - before[1])));
1022 map_center_unit(nunitx, nunity);
1025 g_debug("MAP PAN: %d %d %d %d (%d, %d)", cx, cy, xx, yy, nunitx, nunity);
1032 map_key_zoom_timeout(void)
1034 if (_key_zoom_new < _zoom) {
1035 /* We're currently zooming in (_zoom is decreasing). */
1036 guint test = _key_zoom_new - _curr_repo->view_zoom_steps;
1037 if (test < map_max_zoom)
1038 _key_zoom_new = test;
1042 /* We're currently zooming out (_zoom is increasing). */
1043 guint test = _key_zoom_new + _curr_repo->view_zoom_steps;
1044 if (test < map_max_zoom)
1045 _key_zoom_new = test;
1054 map_information_text(guint x, guint y, GdkGC *gc, gchar *msg)
1056 guint width, height;
1058 pango_layout_set_text(speed_layout, msg, -1);
1059 pango_layout_get_pixel_size(speed_layout, &width, &height);
1060 gtk_widget_queue_draw_area(_map_widget, x - 5, y - 5, width * 3 + 15, height + 5);
1061 gdk_window_process_all_updates();
1062 gdk_draw_layout(_map_widget->window, gc, x, y, speed_layout);
1066 map_speed_draw(void)
1072 cur_speed = _gps->data.speed * UNITS_CONVERT[_units];
1073 gc=(cur_speed > _speed_limit) ? speed_gc1 : speed_gc2;
1074 buffer = g_strdup_printf("%0.0f", cur_speed);
1075 map_information_text(10, 10, gc, buffer);
1080 map_scale_draw(GdkEventExpose *event)
1084 gdouble lat1, lon1, lat2, lon2;
1087 pango_layout_set_text(scale_layout, "0", -1);
1088 pango_layout_get_pixel_size(scale_layout, NULL, &scale_rect.height);
1089 scale_rect.y = _screen_height_pixels - scale_rect.height - 1;
1091 gdk_rectangle_intersect(&event->area, &scale_rect, &event->area);
1093 if (event->area.width && event->area.height) {
1094 gdk_draw_rectangle(_map_widget->window,
1095 _map_widget->style->bg_gc[GTK_WIDGET_STATE(_map_widget)],
1096 TRUE, scale_rect.x, scale_rect.y,
1099 gdk_draw_rectangle(_map_widget->window,
1100 _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
1101 FALSE, scale_rect.x, scale_rect.y,
1105 /* Now calculate and draw the distance. */
1106 unit2latlon(_center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4), _center.unity, lat1, lon1);
1107 unit2latlon(_center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4), _center.unity, lat2, lon2);
1108 distance=calculate_distance(lat1, lon1, lat2, lon2) * UNITS_CONVERT[_units];
1111 g_snprintf(buffer, sizeof(buffer), "%0.2f %s", distance, UNITS_TEXT[_units]);
1112 else if (distance < 10.f)
1113 g_snprintf(buffer, sizeof(buffer), "%0.1f %s", distance, UNITS_TEXT[_units]);
1115 g_snprintf(buffer, sizeof(buffer), "%0.f %s", distance, UNITS_TEXT[_units]);
1117 pango_layout_set_text(scale_layout, buffer, -1);
1118 pango_layout_get_pixel_size(scale_layout, &width, NULL);
1120 /* Draw the layout itself. */
1121 gdk_draw_layout(_map_widget->window,
1122 _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
1123 scale_rect.x + (scale_rect.width - width) / 2,
1124 scale_rect.y, scale_layout);
1126 /* Draw little hashes on the ends. */
1127 gdk_draw_line(_map_widget->window,
1128 _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
1130 scale_rect.y + scale_rect.height / 2 - 4,
1132 scale_rect.y + scale_rect.height / 2 + 4);
1133 gdk_draw_line(_map_widget->window,
1134 _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
1136 scale_rect.y + scale_rect.height / 2,
1137 scale_rect.x + (scale_rect.width - width) / 2 - 4,
1138 scale_rect.y + scale_rect.height / 2);
1139 gdk_draw_line(_map_widget->window,
1140 _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
1141 scale_rect.x + scale_rect.width - 4,
1142 scale_rect.y + scale_rect.height / 2 - 4,
1143 scale_rect.x + scale_rect.width - 4,
1144 scale_rect.y + scale_rect.height / 2 + 4);
1145 gdk_draw_line(_map_widget->window,
1146 _map_widget->style->fg_gc[GTK_WIDGET_STATE(_map_widget)],
1147 scale_rect.x + scale_rect.width - 4,
1148 scale_rect.y + scale_rect.height / 2,
1149 scale_rect.x + (scale_rect.width + width) / 2 + 4,
1150 scale_rect.y + scale_rect.height / 2);
1155 map_cb_expose(GtkWidget *widget, GdkEventExpose *event)
1157 gdk_draw_drawable(GDK_DRAWABLE(_map_widget->window),
1158 _gc[COLORABLE_MARK],
1160 event->area.x + _offsetx, event->area.y + _offsety,
1161 event->area.x, event->area.y,
1162 event->area.width, event->area.height);
1163 map_draw_mark(_gps);
1165 /* Draw scale, if necessary. */
1167 map_scale_draw(event);
1183 if ((nzoom >= 0) && (nzoom < map_max_zoom - 1)) {
1184 map_set_zoom(nzoom);
1204 map_autozoomer(void)
1206 static gfloat z=5.0;
1211 if (zoom_timeout_sid==0)
1214 /* If location is known, use road type and speed for zoom setting */
1215 if (map_loc.valid && map_loc.street) {
1216 switch (map_loc.street->type) {
1231 z=(z+_gps->data.speed+1)/5;
1233 if (z>5) z=5.0; else if (z<1) z=1.0;
1235 g_debug("Setting autozoom to: %f %d S:%f\n", z, iz, _gps->data.speed);
1247 map_set_autozoom(gboolean az, gfloat speed)
1250 zoom_timeout_sid=g_timeout_add(speed<5 ? 2000 : 5000, (GSourceFunc) map_autozoomer, NULL);
1251 MACRO_BANNER_SHOW_INFO(_window, "Autozoom enabled");
1254 if (zoom_timeout_sid) {
1255 g_source_remove(zoom_timeout_sid);
1257 MACRO_BANNER_SHOW_INFO(_window, "Autozoom disabled");
1265 map_draw_route(gint x, gint y)
1267 cmenu_route_add_way(x, y);
1271 map_draw_track(gint x, gint y)
1273 _gps->data.unitx=x2unit((gint) (x + 0.5));
1274 _gps->data.unity=y2unit((gint) (y + 0.5));
1275 unit2latlon(_gps->data.unitx, _gps->data.unity, _gps->data.lat, _gps->data.lon);
1276 _gps->data.speed=20.f;
1277 gps_data_integerize(&_gps->data);
1278 _gps->data.time=time(NULL);
1279 track_add(_track, &_gps->data);
1284 map_set_place_information(osm_way *s, osm_place *mp, osm_place *sp)
1288 if (!s && !mp && !sp) {
1289 g_snprintf(buffer, sizeof(buffer), _("Unknown location"));
1292 g_snprintf(buffer, sizeof(buffer), "%s%s%s%s%s%s%s%s%s%s",
1293 s ? s->name ? s->name : "" : "",
1294 s ? s->ref ? ", " : "" : "",
1295 s ? s->ref ? s->ref : "" : "",
1296 s ? s->int_ref ? " (" : "" : "",
1297 s ? s->int_ref ? s->int_ref : "" : "",
1298 s ? s->int_ref ? ") " : "" : "",
1299 (sp && sp->name) ? " in " : "",
1300 (sp && sp->name) ? sp->name : "",
1301 (mp && mp->name) ? " in " : "",
1302 (mp && mp->name) ? mp->name : "");
1304 gtk_label_set_label(GTK_LABEL(info_banner.location), buffer);
1308 map_update_destination(gdouble lat, gdouble lon)
1312 static gdouble prev_dt=99999.0;
1313 static gboolean dest_reached=FALSE;
1317 dt=calculate_distance(lat, lon, _dest->lat, _dest->lon);
1318 dh=calculate_course(lat, lon, _dest->lat, _dest->lon);
1319 cdist=dt*UNITS_CONVERT[_units];
1321 g_snprintf(buffer, sizeof(buffer), "%.02f %s (%0.02f)", cdist, UNITS_TEXT[_units], dh<0 ? 360+dh : dh);
1322 gtk_label_set_label(GTK_LABEL(info_banner.distance), buffer);
1324 if (dt<0.005 && dest_reached==FALSE) {
1326 announce_destination_reached();
1328 } else if (dt<prev_dt-KM10KNOTS) {
1329 if (_center_mode>0 && _announce_destination==TRUE)
1330 announce_distance_to_destination(cdist, UNITS_TEXT[_units], KM10KNOTS);
1332 } else if (dt>prev_dt+KM10KNOTS/4) {
1338 g_debug("%f (Prev:%f)\n", prev_dt, dt);
1342 gtk_label_set_label(GTK_LABEL(info_banner.distance), "");
1345 gtk_compass_set_dest_heading(_gps_compass, _dest->valid, (gfloat)dh);
1346 gtk_compass_set_dest_heading(_tab_compass, _dest->valid, (gfloat)dh);
1348 if (_route->next_way && _route->next_way->point) {
1349 gdouble wp_lat, wp_lon;
1352 unit2latlon(_route->next_way->point->unitx, _route->next_way->point->unity, wp_lat, wp_lon);
1353 wc=calculate_course(lat, lon, wp_lat, wp_lon);
1354 gtk_compass_set_way_heading(_gps_compass, TRUE, wc);
1355 gtk_compass_set_way_heading(_tab_compass, TRUE, wc);
1357 gtk_compass_set_way_heading(_gps_compass, FALSE, 0);
1358 gtk_compass_set_way_heading(_tab_compass, FALSE, 0);
1364 * Query the OSM database for where we are.
1366 * XXX: This is the wrong place for this function.
1369 map_update_location(gdouble lat, gdouble lon, gboolean force)
1372 static gboolean inp=FALSE;
1374 /* We run the gtk mainloop in progress callback so we can be called again, we don't like that */
1376 g_debug("LOC: Query in progress");
1381 ilat=lat2mp_int(lat);
1382 ilon=lon2mp_int(lon);
1384 if (_gps->data.fix>1 && !force)
1385 osm_set_way_range_from_speed(_gps->data.speed);
1387 osm_set_way_range(2800);
1389 map_update_destination(lat, lon);
1391 if (osm_check_location(&map_loc, ilat, ilon)==FALSE) {
1392 osm_progress_set_widget(_db, _progress_item);
1393 osm_get_location_data(ilat, ilon, _gps->data.heading, &map_loc);
1395 map_set_place_information(map_loc.street, map_loc.primary, map_loc.secondary);
1397 map_set_place_information(NULL, NULL, NULL);
1398 osm_progress_set_widget(_db, NULL);
1399 } else g_debug("IN PLACE");
1405 * Mouse scroller zoom in/out callback
1408 map_cb_scroll_event(GtkWidget * widget, GdkEventScroll * event)
1410 if (event->direction == GDK_SCROLL_UP) {
1411 map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1412 map_set_zoom(_zoom - 1);
1413 } else if (event->direction == GDK_SCROLL_DOWN) {
1414 map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1415 map_set_zoom(_zoom + 1);
1421 * Start map drag operation
1424 map_drag_start(gint x,gint y)
1428 before[0] = press[0];
1429 before[1] = press[1];
1430 _center_mode=CENTER_MANUAL;
1432 g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
1434 map_drag_id=g_signal_connect(G_OBJECT(_map_widget), "motion_notify_event", G_CALLBACK(map_follow_move_cb), NULL);
1438 * Stop map drag operation
1441 map_drag_stop(gint x, gint y)
1446 g_signal_handler_disconnect(G_OBJECT(_map_widget), map_drag_id);
1457 if (map_dragged==TRUE)
1461 /* Workaround hildon content menu problem */
1463 map_cb_show_poi_info_dialog(gpointer data)
1465 guint poi_id=GPOINTER_TO_INT(data);
1466 if (poi_info_dialog(_window, poi_id)==FALSE)
1467 g_printerr("Huh? Failed to display info dialog\n");
1472 map_cb_button_press(GtkWidget *widget, GdkEventButton *event)
1474 _cmenu_position_x = event->x + 0.5;
1475 _cmenu_position_y = event->y + 0.5;
1477 switch (event->button) {
1479 if (event->type==GDK_2BUTTON_PRESS) {
1480 map_center_unit(x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1481 #ifdef MAP_ZOOM_ON_DOUBLE_CLICK
1482 map_set_zoom(_zoom-1);
1484 map_data_needs_refresh=TRUE;
1485 map_drag_stop(event->x, event->y);
1488 if (event->type==GDK_3BUTTON_PRESS) {
1489 map_drag_stop(event->x, event->y);
1493 map_drag_start(event->x, event->y);
1501 gtk_menu_popup(GTK_MENU(_menu_map), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time());
1505 /* Return FALSE to allow context menu to work. */
1510 map_pan_click_check(gint x, gint y)
1514 if (x<MAP_THUMB_MARGIN_X)
1516 else if (x>(_screen_width_pixels-MAP_THUMB_MARGIN_X))
1519 if (y<MAP_THUMB_MARGIN_Y)
1521 else if (y>(_screen_height_pixels-MAP_THUMB_MARGIN_Y))
1524 if (pns!=0 || pew!=0) {
1526 g_debug("MPAN: %d, %d", pns, pew);
1533 map_cb_button_release(GtkWidget *widget, GdkEventButton *event)
1539 switch (event->button) {
1542 #if defined(WITH_HILDON_NEW)
1543 if (hildon_helper_event_button_is_finger(event)) {
1544 g_debug("Thumb down");
1545 if (map_pan_click_check(event->x, event->y))
1551 case MAP_MODE_DRAW_TRACK:
1552 map_draw_track(event->x, event->y);
1554 case MAP_MODE_DRAW_ROUTE:
1555 map_draw_route(event->x, event->y);
1557 case MAP_MODE_SET_ROUTE_FROM:
1558 case MAP_MODE_SET_ROUTE_POINT:
1559 case MAP_MODE_SET_ROUTE_TO:
1562 ux=x2unit(_cmenu_position_x);
1563 uy=y2unit(_cmenu_position_y);
1565 unit2latlon(ux, uy, lat, lon);
1566 if (map_poi_find_at_latlon(lat, lon, &poi_id)==TRUE) {
1567 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_cb_show_poi_info_dialog, GINT_TO_POINTER(poi_id), NULL);
1569 if (map_data_needs_refresh==TRUE) {
1570 map_data_needs_refresh=FALSE;
1572 g_idle_add_full(G_PRIORITY_HIGH_IDLE,(GSourceFunc)map_update_location_from_center, NULL, NULL);
1575 set_action_activate("autocenter_none", TRUE);
1576 map_drag_stop(event->x, event->y);
1581 if (map_pan_click_check(event->x, event->y)==FALSE)
1582 map_set_zoom(_zoom-1);
1590 /* Return FALSE to avoid context menu (if it hasn't popped up already). */
1595 map_dialog_goto_latlon(void)
1603 dialog = gtk_dialog_new_with_buttons(_("Go to Lat/Lon"),
1604 GTK_WINDOW(_window),
1605 GTK_DIALOG_MODAL, GTK_STOCK_OK,
1606 GTK_RESPONSE_ACCEPT,
1608 GTK_RESPONSE_REJECT, NULL);
1610 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table = gtk_table_new(2, 3, FALSE), TRUE, TRUE, 0);
1612 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Latitude")),0, 1, 0, 1, GTK_FILL, 0, 2, 4);
1613 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1615 gtk_table_attach(GTK_TABLE(table), txt_lat = gtk_entry_new(), 1, 2, 0, 1, GTK_FILL, 0, 2, 4);
1616 gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
1618 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Longitude")), 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
1619 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1621 gtk_table_attach(GTK_TABLE(table), txt_lon = gtk_entry_new(), 1, 2, 1, 2, GTK_FILL, 0, 2, 4);
1622 gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
1624 #if defined (WITH_DEVICE_770) && !defined(WITH_HILDON_NEW)
1625 g_object_set(G_OBJECT(txt_lat), HILDON_INPUT_MODE_HINT, HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL);
1626 g_object_set(G_OBJECT(txt_lon), HILDON_INPUT_MODE_HINT, HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL);
1629 /* Initialize with the current center position. */
1633 unit2latlon(_center.unitx, _center.unity, lat, lon);
1634 g_snprintf(buffer, sizeof(buffer), "%.06f", lat);
1635 gtk_label_set_text(GTK_LABEL(txt_lat), buffer);
1636 g_snprintf(buffer, sizeof(buffer), "%.06f", lon);
1637 gtk_label_set_text(GTK_LABEL(txt_lon), buffer);
1640 gtk_widget_show_all(dialog);
1642 while (GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
1648 text = gtk_entry_get_text(GTK_ENTRY(txt_lat));
1649 lat = strtof(text, &error_check);
1650 if (text == error_check || lat < -90.f || lat > 90.f) {
1651 popup_error(dialog, _("Invalid Latitude"));
1655 text = gtk_entry_get_text(GTK_ENTRY(txt_lon));
1656 lon = strtof(text, &error_check);
1657 if (text == error_check || lon < -180.f || lon > 180.f) {
1658 popup_error(dialog, _("Invalid Longitude"));
1662 latlon2unit(lat, lon, unitx, unity);
1663 if (_center_mode > 0)
1664 set_action_activate("autocenter_none", TRUE);
1666 map_center_unit(unitx, unity);
1669 gtk_widget_destroy(dialog);