]> err.no Git - mapper/blob - src/gtkmap.c
More map widget work. Now it compiles.
[mapper] / src / gtkmap.c
1 /*
2  * MapWidget: Display a map
3  * Copyright (C) 2007 Kaj-Michael Lang
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include <stdlib.h>
21 #include <glib/gstdio.h>
22 #include <glib-object.h>
23 #include <math.h>
24 #include <gtk/gtk.h>
25
26 #ifdef WITH_GL
27 #include <GL/gl.h>
28 #include <gtk/gtkgl.h>
29 #endif
30
31 #include "image-cache.h"
32 #include "map-repo.h"
33 #include "latlon.h"
34 #include "gtkmap.h"
35
36 #define MAP_THUMB_MARGIN_X (100)
37 #define MAP_THUMB_MARGIN_Y (75)
38
39 /* Initial size */
40 #define BUF_WIDTH_TILES (4)
41 #define BUF_HEIGHT_TILES (3)
42 #define BUF_WIDTH_PIXELS (1024)
43 #define BUF_HEIGHT_PIXELS (768)
44
45 #define SCALE_WIDTH (300)
46
47 #define MAP_CACHE_DEFAULT (64)
48
49 G_DEFINE_TYPE(GtkMap, gtk_map, GTK_TYPE_WIDGET);
50
51 #define GTK_MAP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_MAP_TYPE, GtkMapPriv))
52
53 typedef struct _GtkMapPriv GtkMapPriv; 
54 struct _GtkMapPriv
55
56         /* Map image buffer */
57         GdkPixmap *buffer;
58         guint buf_width_tiles;
59         guint buf_height_tiles;
60         guint buf_width_pixels;
61         guint buf_height_pixels;
62
63         /* Cairo context for widget->window */
64 #ifdef WITH_CAIRO
65         cairo_t *ct;
66 #endif
67
68         PangoContext *context;
69         PangoLayout *layout;
70         PangoFontDescription *fontdesc;
71
72         PangoContext *speed_context;
73         PangoLayout *speed_layout;
74         PangoFontDescription *speed_font;
75
76         PangoContext *scale_context;
77         PangoLayout *scale_layout;
78         PangoFontDescription *scale_font;
79
80         GdkGC *gc_h;
81         GdkGC *gc_w;
82         GdkGC *gc_d;
83
84         GdkGC *speed_gc1;
85         GdkGC *speed_gc2;
86         GdkGC *speed_gc;
87
88         GdkRectangle scale_rect;
89
90         RepoData *curr_repo;
91
92         GTimer *timer;
93         ImageCache *icache;
94         GList *markers;
95
96         /* OpenGL data */
97 #ifdef WITH_GL
98         GdkGLConfig* gl_config;
99 #endif
100         gboolean gl;
101
102         GdkGC *gc_mark;
103         GdkGC *gc_velvec;
104
105         /* Cached Location dot x,y values */
106         gint mark_x1;
107         gint mark_x2;
108         gint mark_y1;
109         gint mark_y2;
110         GdkRectangle mark_rect;
111
112         GtkMapCenterMode center_mode;
113
114         Point center;
115         Point min_center;
116         Point max_center;
117         Point focus;
118
119         /* Our "location" on the map */
120         Point location;
121
122         guint lead_ratio;
123         guint center_ratio;
124
125         gint vel_offsetx;
126         gint vel_offsety;
127
128         guint base_tilex;
129         guint base_tiley;
130
131         /* Zoom settings */
132         gint zoom;
133         gint max_zoom;
134         gint min_zoom;
135
136         gfloat units_conv;
137         gchar *units_str;
138
139         gfloat speed;
140
141         /* Buffer offset */
142         gint offsetx;
143         gint offsety;
144
145         guint screen_grids_halfwidth;
146         guint screen_grids_halfheight;
147         guint screen_width_pixels;
148         guint screen_height_pixels;
149
150         guint focus_unitwidth;
151         guint focus_unitheight;
152         guint world_size_tiles;
153
154         gint show_paths;
155         gboolean show_scale;
156         gboolean show_velvec;
157         gboolean show_markers;
158
159         guint draw_width;
160
161         gboolean fast_render;
162
163         guint key_zoom_new;
164         guint key_zoom_timeout_sid;
165 };
166
167 #define tile2grid(tile) ((tile) << 3)
168 #define grid2tile(grid) ((grid) >> 3)
169 #define tile2pixel(tile) ((tile) << 8)
170 #define pixel2tile(pixel) ((pixel) >> 8)
171 #define tile2unit(tile) ((tile) << (8 + priv->zoom))
172 #define unit2tile(unit) ((unit) >> (8 + priv->zoom))
173 #define tile2zunit(tile, zoom) ((tile) << (8 + zoom))
174 #define unit2ztile(unit, zoom) ((unit) >> (8 + zoom))
175
176 #define grid2pixel(grid) ((grid) << 5)
177 #define pixel2grid(pixel) ((pixel) >> 5)
178 #define grid2unit(grid) ((grid) << (5 + priv->zoom))
179 #define unit2grid(unit) ((unit) >> (5 + priv->zoom))
180
181 #define pixel2unit(pixel) ((pixel) << priv->zoom)
182 #define unit2pixel(pixel) ((pixel) >> priv->zoom)
183 #define pixel2zunit(pixel, zoom) ((pixel) << (zoom))
184
185 #define unit2bufx(unit) (unit2pixel(unit) - tile2pixel(priv->base_tilex))
186 #define bufx2unit(x) (pixel2unit(x) + tile2unit(priv->base_tilex))
187 #define unit2bufy(unit) (unit2pixel(unit) - tile2pixel(priv->base_tiley))
188 #define bufy2unit(y) (pixel2unit(y) + tile2unit(priv->base_tiley))
189
190 #define unit2x(unit) (unit2pixel(unit) - tile2pixel(priv->base_tilex) - priv->offsetx)
191 #define x2unit(x) (pixel2unit(x + priv->offsetx) + tile2unit(priv->base_tilex))
192 #define unit2y(unit) (unit2pixel(unit) - tile2pixel(priv->base_tiley) - priv->offsety)
193 #define y2unit(y) (pixel2unit(y + priv->offsety) + tile2unit(priv->base_tiley))
194
195 #define leadx2unit (priv->location.unitx + (priv->lead_ratio) * pixel2unit(priv->vel_offsetx))
196 #define leady2unit (priv->location.unity + (0.6f*priv->lead_ratio) * pixel2unit(priv->vel_offsety))
197
198 #define GTK_MAP_TILE_SIZE_PIXELS (256)
199 #define GTK_MAP_TILE_SIZE_P2 (8)
200
201 /* #define GTK_MAP_WORLD_SIZE_UNITS(max_zoom) (2 << (max_zoom + GTK_MAP_TILE_SIZE_P2)) */
202
203 #define GTK_MAP_WORLD_SIZE_UNITS (1<<31)
204 #define WORLD_SIZE_UNITS GTK_MAP_WORLD_SIZE_UNITS
205
206 /* Pans are done two "grids" at a time, or 64 pixels. */
207 #define GTK_MAP_PAN_UNITS (grid2unit(2))
208
209 #define BOUND(x, a, b) { \
210         if((x) < (a)) \
211                 (x) = (a); \
212         else if((x) > (b)) \
213                 (x) = (b); \
214 }
215
216 static void gtk_map_finalize(GObject *object);
217
218 static void gtk_map_size_request(GtkWidget *widget, GtkRequisition *requisition);
219 static void gtk_map_size_allocate(GtkWidget *widget, GtkAllocation *allocate);
220 static void gtk_map_realize(GtkWidget *widget);
221 static gboolean gtk_map_expose(GtkWidget *widget, GdkEventExpose *event);
222 static gboolean gtk_map_configure(GtkWidget *widget, GdkEventConfigure *event);
223
224 static void gtk_map_scale_draw(GtkWidget *widget, GdkEventExpose *event);
225 static void gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event);
226
227 static gboolean gtk_map_update_buffer_size(GtkMap *map, gint new_width, gint new_height);
228
229 static void gtk_map_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
230 static void gtk_map_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
231
232 /* Signal IDs */
233 enum {
234         MAP_LOCATION_CHANGED,
235         
236         MAP_ZOOMED_IN,
237         MAP_ZOOMED_OUT,
238
239         MAP_PANNED,
240
241         MARKER_CLICK,
242
243         LAST_SIGNAL
244 };
245
246 /* Property IDs */
247 enum {
248         LAST_PROP
249 };
250
251 static guint gtk_map_signals[LAST_SIGNAL] = { 0 };
252
253 static void
254 gtk_map_class_init (GtkMapClass *class)
255 {
256 GObjectClass *object_class;
257 GtkWidgetClass *widget_class;
258         
259 object_class = (GObjectClass*) class;
260 widget_class = (GtkWidgetClass*) class;
261         
262 object_class->finalize = gtk_map_finalize;
263 object_class->set_property = gtk_map_set_property;
264 object_class->get_property = gtk_map_get_property;
265         
266 widget_class->size_request = gtk_map_size_request;
267 widget_class->expose_event = gtk_map_expose;
268 widget_class->configure_event = gtk_map_configure;
269 widget_class->realize = gtk_map_realize;
270 widget_class->size_allocate = gtk_map_size_allocate;
271
272 g_type_class_add_private (object_class, sizeof(GtkMapPriv));
273 }
274
275 static inline void 
276 gtk_map_recalc_center(GtkMapPriv *priv)
277 {
278 g_return_if_fail(priv);
279 switch(priv->center_mode) {
280         case CENTER_LEAD:
281                 priv->center.unitx = leadx2unit;
282                 priv->center.unity = leady2unit;
283         break;
284         case CENTER_LATLON:
285                 priv->center.unitx = priv->location.unitx;
286                 priv->center.unity = priv->location.unity;
287         break;
288         default:
289 #if 0
290                 priv->center.unitx = center->unitx;
291                 priv->center.unity = center->unity;
292 #endif
293         break;
294 }
295 }
296
297 static inline void
298 gtk_map_recalc_offset(GtkMapPriv *priv)
299 {
300 g_return_if_fail(priv);
301 priv->offsetx = grid2pixel(unit2grid(priv->center.unitx) - priv->screen_grids_halfwidth - tile2grid(priv->base_tilex));
302 priv->offsety = grid2pixel(unit2grid(priv->center.unity) - priv->screen_grids_halfheight - tile2grid(priv->base_tiley));
303 }
304
305 static inline void 
306 gtk_map_recalc_focus_base(GtkMapPriv *priv)
307 {
308 g_return_if_fail(priv);
309 priv->focus.unitx = x2unit(priv->screen_width_pixels * priv->center_ratio / 20);
310 priv->focus.unity = y2unit(priv->screen_height_pixels * priv->center_ratio / 20);
311 }
312
313 static inline void
314 gtk_map_recalc_focus_size(GtkMapPriv *priv)
315 {
316 g_return_if_fail(priv);
317 priv->focus_unitwidth = pixel2unit((10 - priv->center_ratio) * priv->screen_width_pixels / 10);
318 priv->focus_unitheight = pixel2unit((10 - priv->center_ratio) * priv->screen_height_pixels / 10);
319 }
320
321 static inline void 
322 gtk_map_recalc_center_bounds(GtkMapPriv *priv)
323 {
324 g_return_if_fail(priv);
325 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
326 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
327 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfwidth) - 1;
328 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfheight) - 1;
329 }
330
331 static void
332 gtk_map_init(GtkMap *map)
333 {
334 GtkMapPriv *priv;
335 GdkColor color;
336
337 g_debug("GTKMAP: Init");
338 priv=GTK_MAP_GET_PRIVATE(map);
339
340 #ifdef WITH_CAIRO
341 priv->ct=gdk_cairo_create(GTK_WIDGET(map)->window);
342 #endif
343
344 priv->zoom=3;
345 priv->center_mode=CENTER_LATLON;
346 priv->base_tilex=-5;
347 priv->base_tiley=-5;
348 priv->draw_width=4;
349
350 priv->speed=-1;
351 priv->speed_gc=priv->speed_gc1;
352
353 priv->icache=image_cache_new(64);
354
355 priv->scale_context=gtk_widget_get_pango_context(GTK_WIDGET(map));
356 priv->scale_layout=pango_layout_new(priv->scale_context);
357 priv->scale_font=pango_font_description_new();
358 pango_font_description_set_size(priv->scale_font, 12 * PANGO_SCALE);
359 pango_layout_set_font_description(priv->scale_layout, priv->scale_font);
360
361 /* Speed limit, over limit color */
362 priv->speed_gc1=gdk_gc_new(GTK_WIDGET(map)->window);
363 color.red=0xffff;
364 color.green=0;
365 color.blue=0;
366 gdk_gc_set_rgb_fg_color(priv->speed_gc1, &color);
367
368 /* Speed limit, under limit color */
369 priv->speed_gc2=gdk_gc_new(GTK_WIDGET(map)->window);
370 color.red=0;
371 color.green=0x1000;
372 color.blue=0;
373 gdk_gc_set_rgb_fg_color(priv->speed_gc2, &color);
374
375 priv->speed_context=gtk_widget_get_pango_context(GTK_WIDGET(map));
376 priv->speed_layout=pango_layout_new(priv->speed_context);
377 priv->speed_font=pango_font_description_new();
378 pango_font_description_set_size(priv->speed_font, 48 * PANGO_SCALE);
379 pango_layout_set_font_description(priv->speed_layout, priv->speed_font);
380 pango_layout_set_alignment(priv->speed_layout, PANGO_ALIGN_LEFT);
381
382 priv->gl=FALSE;
383
384 #ifdef WITH_GL
385 priv->gl_config=gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
386 if (priv->gl_config) {
387     g_print("OpenGL version: %s\n", glGetString (GL_VERSION));
388     g_print("OpenGL vendor: %s\n", glGetString (GL_VENDOR));
389     g_print("OpenGL renderer: %s\n", glGetString (GL_RENDERER));
390         gtk_widget_set_gl_capability(map->widget, priv->gl_config, NULL, TRUE, GDK_GL_RGBA_TYPE);
391         priv->gl=TRUE;
392 }
393 #endif
394
395 priv->buffer=NULL;
396
397 gtk_widget_set_extension_events(GTK_WIDGET(map), GDK_EXTENSION_EVENTS_ALL);
398
399 gtk_widget_add_events(GTK_WIDGET(map), GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
400         | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
401
402 #if 0
403 g_signal_connect(G_OBJECT(map), "button_press_event", G_CALLBACK(gtk_map_cb_button_press), NULL);
404 #endif
405
406 }
407
408 static gboolean 
409 gtk_map_configure(GtkWidget *widget, GdkEventConfigure *event)
410 {
411 guint tw, th;
412 GtkMap *map;
413 GtkMapPriv *priv;
414
415 g_return_val_if_fail(GTK_IS_MAP(widget), TRUE);
416 map=GTK_MAP(widget);
417 priv=GTK_MAP_GET_PRIVATE(map);
418
419 tw=GTK_MAP_TILE_SIZE_PIXELS*((widget->allocation.width/GTK_MAP_TILE_SIZE_PIXELS)+2);
420 th=GTK_MAP_TILE_SIZE_PIXELS*((widget->allocation.height/GTK_MAP_TILE_SIZE_PIXELS)+2);
421
422 gtk_map_update_buffer_size(GTK_MAP(widget), tw, th);
423 g_assert(priv->buffer);
424
425 priv->buf_width_pixels=tw;
426 priv->buf_height_pixels=th;
427 priv->buf_width_tiles=priv->buf_width_pixels/GTK_MAP_TILE_SIZE_PIXELS;
428 priv->buf_height_tiles=priv->buf_height_pixels/GTK_MAP_TILE_SIZE_PIXELS;
429
430 priv->screen_width_pixels = widget->allocation.width;
431 priv->screen_height_pixels = widget->allocation.height;
432 priv->screen_grids_halfwidth = pixel2grid(priv->screen_width_pixels) / 2;
433 priv->screen_grids_halfheight = pixel2grid(priv->screen_height_pixels) / 2;
434
435 /* Set scale_rect. */
436 priv->scale_rect.x = (priv->screen_width_pixels - SCALE_WIDTH) / 2;
437 priv->scale_rect.width = SCALE_WIDTH;
438
439 gtk_map_recalc_focus_base(priv);
440 gtk_map_recalc_focus_size(priv);
441
442 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
443 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
444 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfwidth) - 1;
445 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfheight) - 1;
446
447 return TRUE;
448 }
449
450
451 static void
452 gtk_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
453 {
454 GtkMap *map;
455 g_return_if_fail(GTK_IS_MAP(object));
456 map=GTK_MAP(object);
457 switch (prop_id) {
458         default:
459                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
460         break;
461 }
462 }
463
464 static void
465 gtk_map_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
466 {
467 GtkMap *map;
468 g_return_if_fail(GTK_IS_MAP(object));
469 map=GTK_MAP(object);
470 switch (prop_id) {
471         default:
472                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
473         break;
474 }
475 }
476
477 GtkWidget*
478 gtk_map_new(void)
479 {
480 return g_object_new(GTK_MAP_TYPE, NULL);
481 }
482
483 static void
484 gtk_map_finalize(GObject *object)
485 {
486 GtkMap *map;
487         
488 g_return_if_fail(GTK_IS_MAP(object));
489 map=GTK_MAP(object);
490
491 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
492         gtk_widget_unmap(GTK_WIDGET(object));
493 }
494
495 G_OBJECT_CLASS(gtk_map_parent_class)->finalize(object);
496 }
497
498 static void
499 gtk_map_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
500 {
501 GtkMap *map;
502         
503 g_return_if_fail(GTK_IS_MAP(widget));
504 map=GTK_MAP(widget);
505 }
506
507 static void
508 gtk_map_size_request(GtkWidget  *widget, GtkRequisition *requisition)
509 {
510 GtkMap *map;
511         
512 g_return_if_fail(GTK_IS_MAP(widget));
513 g_return_if_fail(requisition != NULL);
514         
515 map=GTK_MAP(widget);
516         
517 requisition->width=512;
518 requisition->height=256;
519 map->width=512;
520 map->height=256;
521 }
522
523 static gboolean
524 gtk_map_update_buffer_size(GtkMap *map, gint new_width, gint new_height)
525 {
526 GtkMapPriv *priv;
527
528 priv=GTK_MAP_GET_PRIVATE(map);
529
530 if (priv->buffer==NULL) {
531         priv->buffer=gdk_pixmap_new(GTK_WIDGET(map)->window, new_width, new_height, -1);
532         return TRUE;
533 } else if (new_width>priv->buf_width_pixels || new_height>priv->buf_height_pixels || 
534                 new_width<priv->buf_width_pixels-(GTK_MAP_TILE_SIZE_PIXELS*2) || new_height<priv->buf_height_pixels-(GTK_MAP_TILE_SIZE_PIXELS*2) ) {
535         g_object_unref(priv->buffer);
536         priv->buffer=gdk_pixmap_new(GTK_WIDGET(map)->window, new_width, new_height, -1);
537         return TRUE;
538 }
539 return FALSE;
540 }
541
542 static void 
543 gtk_map_realize(GtkWidget *widget)
544 {
545 GtkMap *map;
546         
547 g_return_if_fail(GTK_IS_MAP(widget));
548 map=GTK_MAP(widget);
549 }
550
551 static gboolean
552 gtk_map_expose(GtkWidget *widget, GdkEventExpose *event)
553 {
554 GtkMap *map;
555 GtkMapPriv *priv;
556 GtkStyle *style;
557
558 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
559 g_return_val_if_fail(event != NULL, FALSE);
560
561 map=GTK_MAP(widget);
562 priv=GTK_MAP_GET_PRIVATE(map);
563
564 g_return_val_if_fail(priv->buffer, FALSE);
565
566 style=widget->style;
567
568 gdk_draw_drawable(GDK_DRAWABLE(map),
569                 style->fg_gc[GTK_STATE_NORMAL],
570                 priv->buffer,
571                 event->area.x + priv->offsetx, 
572                 event->area.y + priv->offsety,
573                 event->area.x, 
574                 event->area.y,
575                 event->area.width, 
576                 event->area.height);
577
578 #if 0
579 gtk_map_paths_draw(widget, event);
580
581 gtk_map_markers_draw(widget, event);
582
583 gtk_map_speed_draw(widget, event);
584 #endif
585
586 gtk_map_mark_draw(widget, event);
587 gtk_map_scale_draw(widget, event);
588
589 return TRUE;
590 }
591
592 static void 
593 gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event)
594 {
595 GtkMap *map;
596 GtkMapPriv *priv;
597
598 map=GTK_MAP(widget);
599 priv=GTK_MAP_GET_PRIVATE(map);
600
601 #if 0
602 if (!priv->draw_mark)
603         return;
604 #endif
605
606 if (!gdk_rectangle_intersect(&event->area, &priv->mark_rect, &event->area))
607         return;
608
609 #ifdef WITH_CAIRO
610 cairo_arc(priv->ct, priv->mark_x1, priv->marky1, priv->draw_width*2, 0, 2 * M_PI);
611 cairo_set_source_rgb(priv->ct, 1, 1, 1);
612 cairo_fill_preserve(priv->ct);
613 cairo_set_source_rgb(priv->ct, 0, 0, 0);
614 if (priv->show_velvec) {
615         cairo_save(priv->ct);
616         cairo_set_line_width(priv->ct, priv->draw_width);
617         cairo_move_to(priv->ct, priv->mark_x1, priv->marky1);
618         cairo_line_to(priv->ct, priv->mark_x2, priv->marky2);
619         cairo_restore(priv->ct);
620 }
621 cairo_stroke(priv->ct);
622 #else
623 gdk_draw_arc(widget->window,
624         priv->gc_mark,
625         FALSE,
626         priv->mark_x1 - priv->draw_width,
627         priv->mark_y1 - priv->draw_width,
628         2 * priv->draw_width,
629         2 * priv->draw_width,
630         0, 360 * 64);
631
632 if (priv->show_velvec)
633         gdk_draw_line(widget->window, 
634                 priv->gc_velvec,
635                 priv->mark_x1, priv->mark_y1, 
636                 priv->mark_x2, priv->mark_y2);
637 #endif
638 }
639
640 static void 
641 gtk_map_calculate_mark(GtkWidget *widget)
642 {
643 GtkMap *map;
644 GtkMapPriv *priv;
645
646 map=GTK_MAP(widget);
647 priv=GTK_MAP_GET_PRIVATE(map);
648
649 priv->mark_x1 = unit2x(priv->location.unitx);
650 priv->mark_y1 = unit2y(priv->location.unity);
651 priv->mark_x2 = priv->mark_x1 + (priv->show_velvec ? priv->vel_offsetx : 0);
652 priv->mark_y2 = priv->mark_y1 + (priv->show_velvec ? priv->vel_offsety : 0);
653
654 priv->mark_rect.x = MIN(priv->mark_x1, priv->mark_x2) - (2 * priv->draw_width);
655 priv->mark_rect.y = MIN(priv->mark_y1, priv->mark_y2) - (2 * priv->draw_width);
656 priv->mark_rect.width = abs(priv->mark_x1 - priv->mark_x2) + (4 * priv->draw_width);
657 priv->mark_rect.height = abs(priv->mark_y1 - priv->mark_y2) + (4 * priv->draw_width);
658 }
659
660 static void 
661 gtk_map_scale_draw(GtkWidget *widget, GdkEventExpose *event)
662 {
663 GtkMap *map;
664 GtkMapPriv *priv;
665 gchar buffer[16];
666 gdouble distance;
667 gdouble lat1, lon1, lat2, lon2;
668 gint width;
669
670 g_return_if_fail(GTK_IS_MAP(widget));
671 map=GTK_MAP(widget);
672 priv=GTK_MAP_GET_PRIVATE(map);
673
674 pango_layout_set_text(priv->scale_layout, "0", -1);
675 pango_layout_get_pixel_size(priv->scale_layout, NULL, &priv->scale_rect.height);
676 priv->scale_rect.y = priv->screen_height_pixels - priv->scale_rect.height - 1;
677
678 gdk_rectangle_intersect(&event->area, &priv->scale_rect, &event->area);
679
680 if (event->area.width && event->area.height) {
681         gdk_draw_rectangle(widget->window,
682                            widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
683                            TRUE, priv->scale_rect.x, priv->scale_rect.y,
684                            priv->scale_rect.width,
685                            priv->scale_rect.height);
686         gdk_draw_rectangle(widget->window,
687                            widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
688                            FALSE, priv->scale_rect.x, priv->scale_rect.y,
689                            priv->scale_rect.width,
690                            priv->scale_rect.height);
691
692         /* Now calculate and draw the distance. */
693         unit2latlon(priv->center.unitx - pixel2unit(SCALE_WIDTH / 2 - 4), priv->center.unity, lat1, lon1);
694         unit2latlon(priv->center.unitx + pixel2unit(SCALE_WIDTH / 2 - 4), priv->center.unity, lat2, lon2);
695         distance=calculate_distance(lat1, lon1, lat2, lon2) * priv->units_conv;
696
697         if (distance < 1.f)
698                 g_snprintf(buffer, sizeof(buffer), "%0.2f %s", distance, priv->units_str);
699         else if (distance < 10.f)
700                 g_snprintf(buffer, sizeof(buffer), "%0.1f %s", distance, priv->units_str);
701         else
702                 g_snprintf(buffer, sizeof(buffer), "%0.f %s", distance, priv->units_str);
703
704         pango_layout_set_text(priv->scale_layout, buffer, -1);
705         pango_layout_get_pixel_size(priv->scale_layout, &width, NULL);
706
707         /* Draw the layout itself. */
708         gdk_draw_layout(widget->window,
709                         widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
710                         priv->scale_rect.x + (priv->scale_rect.width - width) / 2,
711                         priv->scale_rect.y, priv->scale_layout);
712
713         /* Draw little hashes on the ends. */
714         gdk_draw_line(widget->window,
715                     widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
716                     priv->scale_rect.x + 4,
717                     priv->scale_rect.y + priv->scale_rect.height / 2 - 4,
718                     priv->scale_rect.x + 4,
719                     priv->scale_rect.y + priv->scale_rect.height / 2 + 4);
720         gdk_draw_line(widget->window,
721                     widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
722                         priv->scale_rect.x + 4,
723                     priv->scale_rect.y + priv->scale_rect.height / 2,
724                     priv->scale_rect.x + (priv->scale_rect.width - width) / 2 - 4,
725                     priv->scale_rect.y + priv->scale_rect.height / 2);
726         gdk_draw_line(widget->window,
727                     widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
728                     priv->scale_rect.x + priv->scale_rect.width - 4,
729                     priv->scale_rect.y + priv->scale_rect.height / 2 - 4,
730                     priv->scale_rect.x + priv->scale_rect.width - 4,
731                     priv->scale_rect.y + priv->scale_rect.height / 2 + 4);
732         gdk_draw_line(widget->window,
733                     widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
734                     priv->scale_rect.x + priv->scale_rect.width - 4,
735                     priv->scale_rect.y + priv->scale_rect.height / 2,
736                     priv->scale_rect.x + (priv->scale_rect.width + width) / 2 + 4,
737                     priv->scale_rect.y + priv->scale_rect.height / 2);
738         }
739 }
740
741 static void
742 gtk_map_information_text(GtkWidget *widget, guint x, guint y, GdkGC *gc, gchar *msg)
743 {
744 GtkMap *map;
745 GtkMapPriv *priv;
746 guint width, height;
747
748 g_return_if_fail(GTK_IS_MAP(widget));
749 map=GTK_MAP(widget);
750 priv=GTK_MAP_GET_PRIVATE(map);
751
752 pango_layout_set_text(priv->speed_layout, msg, -1);
753 pango_layout_get_pixel_size(priv->speed_layout, &width, &height);
754 gtk_widget_queue_draw_area(widget, x - 5, y - 5, width * 3 + 15, height + 5);
755 gdk_window_process_all_updates();
756 gdk_draw_layout(widget->window, gc, x, y, priv->speed_layout);
757 gdk_window_process_all_updates();
758 }
759
760 static void
761 gtk_map_speed_draw(GtkWidget *widget, GdkEventExpose *event)
762 {
763 GtkMap *map;
764 GtkMapPriv *priv;
765 gchar buffer[16];
766
767 g_return_if_fail(GTK_IS_MAP(widget));
768 map=GTK_MAP(widget);
769 priv=GTK_MAP_GET_PRIVATE(map);
770
771 if (priv->speed<0)
772         return;
773
774 g_snprintf(buffer, sizeof(buffer), "%0.0f %s", priv->speed * priv->units_conv, priv->units_str);
775 map_information_text(10, 10, priv->speed_gc, buffer);
776 }
777
778 void
779 gtk_map_set_speed(GtkWidget *widget, gfloat speed, gboolean overspeed)
780 {
781 GtkMap *map;
782 GtkMapPriv *priv;
783
784 g_return_if_fail(GTK_IS_MAP(widget));
785 map=GTK_MAP(widget);
786 priv=GTK_MAP_GET_PRIVATE(map);
787
788 priv->speed_gc=(overspeed) ? priv->speed_gc1 : priv->speed_gc2;
789 priv->speed=speed;
790 }
791
792 /**
793  * Do an in-place scaling of a pixbuf's pixels at the given ratio from the given source location.
794  */
795 static void
796 gtk_map_pixbuf_scale_inplace(GdkPixbuf *pixbuf, guint ratio_p2, guint src_x, guint src_y)
797 {
798 guint dest_x = 0, dest_y = 0, dest_dim = GTK_MAP_TILE_SIZE_PIXELS;
799 guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
800 guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
801 guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
802
803 /* Sweep through the entire dest area, copying as necessary, but
804  * DO NOT OVERWRITE THE SOURCE AREA.  We'll copy it afterward. */
805 do {
806         guint src_dim = dest_dim >> ratio_p2;
807         guint src_endx = src_x - dest_x + src_dim;
808         gint x, y;
809
810         for (y = dest_dim - 1; y >= 0; y--) {
811                 guint src_offset_y, dest_offset_y;
812
813                 src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
814                 dest_offset_y = (dest_y + y) * rowstride;
815                 x = dest_dim - 1;
816
817                 if ((unsigned)(dest_y + y - src_y) < src_dim && (unsigned)(dest_x + x - src_x) < src_dim)
818                         x -= src_dim;
819
820                 for (; x >= 0; x--) {
821                         guint src_offset, dest_offset, i;
822
823                         src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
824                         dest_offset = dest_offset_y + (dest_x + x) * n_channels;
825
826                         pixels[dest_offset] = pixels[src_offset];
827                         for (i = n_channels - 1; i; i--)
828                                 pixels[dest_offset + i] = pixels[src_offset + i];
829
830                         if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
831                                 x -= src_dim;
832                 }
833         }
834
835         /* Reuse src_dim and src_endx to store new src_x and src_y. */
836         src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
837         src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
838         dest_x = src_x;
839         dest_y = src_y;
840         src_x = src_dim;
841         src_y = src_endx;
842 }
843 while ((dest_dim >>= ratio_p2) > 1);
844 }
845
846 static gboolean
847 gtk_map_render_tile(GtkWidget *widget, guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
848 {
849 GtkMap *map;
850 GtkMapPriv *priv;
851 GdkPixbuf *pixbuf=NULL;
852 gint zoff;
853
854 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
855 map=GTK_MAP(widget);
856 priv=GTK_MAP_GET_PRIVATE(map);
857
858 g_return_val_if_fail(priv->buffer, FALSE);
859 g_return_val_if_fail(priv->curr_repo, FALSE);
860
861 if (destx > priv->buf_width_pixels || desty > priv->buf_height_pixels)
862         return FALSE;
863
864 if (tilex > priv->world_size_tiles || tiley > priv->world_size_tiles)
865         return FALSE;
866
867 /* g_debug("MAP RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, priv->world_size_tiles, destx, desty, buf_width_tiles, buf_height_tiles); */
868
869 for (zoff = (priv->curr_repo->double_size ? 1 : 0); !pixbuf && (priv->zoom + zoff) <= priv->max_zoom && zoff <= GTK_MAP_TILE_SIZE_P2; zoff += 1) {
870         pixbuf=gtk_map_tile_load(map, tilex, tiley, zoff, !fast_fail);
871         if (!pixbuf) {
872                 if (!fast_fail)
873                         fast_fail=TRUE;
874         } else {
875                 /* Check if we need to blit. */
876                 if (zoff) {
877                         gtk_map_pixbuf_scale_inplace(pixbuf, zoff,
878                                 (tilex - ((tilex >> zoff) << zoff)) << (GTK_MAP_TILE_SIZE_P2 - zoff),
879                                 (tiley - ((tiley >> zoff) << zoff)) << (GTK_MAP_TILE_SIZE_P2 - zoff));
880                         image_cache_invalidate_by_image(priv->icache, pixbuf);
881                 }
882         }
883 }
884
885 if (pixbuf) {
886         gdk_draw_pixbuf(priv->buffer, 
887             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
888                 pixbuf, 0, 0, destx, desty, 
889                 GTK_MAP_TILE_SIZE_PIXELS, 
890                 GTK_MAP_TILE_SIZE_PIXELS, 
891                 GDK_RGB_DITHER_NONE, 0, 0);
892         g_object_unref(pixbuf);
893         return TRUE;
894 }
895 gdk_draw_rectangle(priv->buffer, 
896         widget->style->black_gc, TRUE, 
897         destx, desty, 
898         GTK_MAP_TILE_SIZE_PIXELS, 
899         GTK_MAP_TILE_SIZE_PIXELS);
900 return TRUE;
901 }
902
903 gboolean
904 gtk_map_set_center(GtkWidget *widget, guint new_center_unitx, guint new_center_unity)
905 {
906 GtkMap *map;
907 GtkMapPriv *priv;
908 GtkStyle *style;
909
910 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
911
912 map=GTK_MAP(widget);
913 priv=GTK_MAP_GET_PRIVATE(map);
914 style=widget->style;
915
916 gint new_base_tilex, new_base_tiley;
917 guint new_x, new_y;
918 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
919
920 /* Assure that _center.unitx/y are bounded. */
921 BOUND(new_center_unitx, priv->min_center.unitx, priv->max_center.unitx);
922 BOUND(new_center_unity, priv->min_center.unity, priv->max_center.unity);
923
924 priv->center.unitx = new_center_unitx;
925 priv->center.unity = new_center_unity;
926
927 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) priv->center.unitx)) - (gint)priv->screen_grids_halfwidth);
928 new_base_tiley = grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
929
930 /* Same zoom level, so it's likely that we can reuse some of the old buffer's pixels. */
931
932 if (new_base_tilex != priv->base_tilex || new_base_tiley != priv->base_tiley) {
933         /* If copying from old parts to new parts, we need to make sure we
934          * don't overwrite the old parts when copying, so set up new_x,
935          * new_y, old_x, old_y, iox, and ioy with that in mind. */
936         if (new_base_tiley < priv->base_tiley) {
937                 /* New is lower than old - start at bottom and go up. */
938                 new_y = priv->buf_height_tiles - 1;
939                 ioy = -1;
940         } else {
941                 /* New is higher than old - start at top and go down. */
942                 new_y = 0;
943                 ioy = 1;
944         }
945         if (new_base_tilex < priv->base_tilex) {
946                 /* New is righter than old - start at right and go left. */
947                 base_new_x = priv->buf_width_tiles - 1;
948                 iox = -1;
949         } else {
950                 /* New is lefter than old - start at left and go right. */
951                 base_new_x = 0;
952                 iox = 1;
953         }
954
955         /* Iterate over the y tile values. */
956         old_y = new_y + new_base_tiley - priv->base_tiley;
957         base_old_x = base_new_x + new_base_tilex - priv->base_tilex;
958         priv->base_tilex = new_base_tilex;
959         priv->base_tiley = new_base_tiley;
960
961         for (j = 0; j < priv->buf_height_tiles; ++j, new_y += ioy, old_y += ioy) {
962                 new_x = base_new_x;
963                 old_x = base_old_x;
964                 /* Iterate over the x tile values. */
965                 for (k = 0; k < priv->buf_width_tiles; ++k, new_x += iox, old_x += iox) {
966                         /* Can we get this grid block from the old buffer?. */
967                         if (old_x >= 0 && old_x < priv->buf_width_tiles && old_y >= 0 && old_y < priv->buf_height_tiles) {
968                                 /* Copy from old buffer to new buffer. */
969                                 gdk_draw_drawable(priv->buffer,
970                                             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
971                                                 priv->buffer,
972                                                 old_x * GTK_MAP_TILE_SIZE_PIXELS,
973                                                 old_y * GTK_MAP_TILE_SIZE_PIXELS,
974                                                 new_x * GTK_MAP_TILE_SIZE_PIXELS,
975                                                 new_y * GTK_MAP_TILE_SIZE_PIXELS,
976                                                 GTK_MAP_TILE_SIZE_PIXELS,
977                                                 GTK_MAP_TILE_SIZE_PIXELS);
978                         } else {
979                                 gtk_map_render_tile(GTK_MAP(map), 
980                                                 new_base_tilex + new_x,
981                                                 new_base_tiley + new_y,
982                                                 new_x * GTK_MAP_TILE_SIZE_PIXELS,
983                                                 new_y * GTK_MAP_TILE_SIZE_PIXELS,
984                                                 priv->fast_render);
985                         }
986                 }
987         }
988 }
989
990 gtk_map_recalc_offset(priv);
991 gtk_map_recalc_focus_base(priv);
992
993 gtk_map_refresh(map);
994 }
995
996 void
997 gtk_map_refresh(GtkWidget *widget)
998 {
999 GtkMap *map;
1000
1001 g_return_if_fail(GTK_IS_MAP(widget));
1002
1003 map=GTK_MAP(widget);
1004 gtk_widget_queue_draw_area(widget, 0, 0, map->width, map->height);
1005 }
1006
1007 gint 
1008 gtk_map_get_zoom(GtkWidget *widget)
1009 {
1010 GtkMap *map;
1011 GtkMapPriv *priv;
1012
1013 g_return_val_if_fail(GTK_IS_MAP(widget), -1);
1014
1015 map=GTK_MAP(widget);
1016 priv=GTK_MAP_GET_PRIVATE(map);
1017
1018 return priv->zoom;
1019 }
1020
1021 gboolean
1022 gtk_map_set_zoom(GtkWidget *widget, gint new_zoom)
1023 {
1024 GtkMap *map;
1025 GtkMapPriv *priv;
1026
1027 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1028
1029 map=GTK_MAP(widget);
1030 priv=GTK_MAP_GET_PRIVATE(map);
1031
1032 if (new_zoom > (priv->max_zoom - 1))
1033         return FALSE;
1034
1035 if (new_zoom == priv->zoom)
1036         return FALSE;
1037
1038 priv->zoom = new_zoom / priv->curr_repo->view_zoom_steps * priv->curr_repo->view_zoom_steps;
1039 priv->world_size_tiles = unit2tile(GTK_MAP_WORLD_SIZE_UNITS);
1040
1041 /* If we're leading, update the center to reflect new zoom level. */
1042 gtk_map_recalc_center(priv);
1043
1044 /* Update center bounds to reflect new zoom level. */
1045 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
1046 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
1047 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfwidth) - 1;
1048 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfheight) - 1;
1049
1050 BOUND(priv->center.unitx, priv->min_center.unitx, priv->max_center.unitx);
1051 BOUND(priv->center.unity, priv->min_center.unity, priv->max_center.unity);
1052
1053 priv->base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) priv->center.unitx)) - (gint) priv->screen_grids_halfwidth);
1054 priv->base_tiley = grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
1055
1056 /* New zoom level, so we can't reuse the old buffer's pixels. Update state variables. */
1057 gtk_map_recalc_offset(priv);
1058 gtk_map_recalc_focus_base(priv);
1059 gtk_map_recalc_focus_size(priv);
1060
1061 gtk_map_refresh(GTK_WIDGET(map));
1062
1063 return TRUE;
1064 }
1065
1066 gint 
1067 gtk_map_zoom(GtkWidget *widget, gint zdir)
1068 {
1069 gint nzoom;
1070 GtkMap *map;
1071 GtkMapPriv *priv;
1072
1073 g_return_val_if_fail(GTK_IS_MAP(widget), -1);
1074
1075 map=GTK_MAP(widget);
1076 priv=GTK_MAP_GET_PRIVATE(map);
1077
1078 nzoom=priv->zoom+zdir;
1079 if ((nzoom >= 0) && (nzoom < priv->max_zoom - 1)) {
1080         gtk_map_set_zoom(widget, nzoom);
1081 }
1082 return nzoom;
1083 }
1084
1085 gboolean
1086 gtk_map_zoom_in(GtkWidget *widget)
1087 {
1088 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1089 gtk_map_zoom(widget, -1);
1090 return FALSE;
1091 }
1092
1093 gboolean
1094 gtk_map_zoom_out(GtkWidget *widget)
1095 {
1096 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1097 gtk_map_zoom(widget, 1);
1098 return FALSE;
1099 }
1100
1101 void
1102 gtk_map_set_cache_size(GtkWidget *widget, guint cache_size)
1103 {
1104 GtkMap *map;
1105 GtkMapPriv *priv;
1106
1107 g_return_if_fail(GTK_IS_MAP(widget));
1108 map=GTK_MAP(widget);
1109 priv=GTK_MAP_GET_PRIVATE(map);
1110 if (cache_size>512)
1111         cache_size=512;
1112 image_cache_set_size(priv->icache, cache_size);
1113 }