]> err.no Git - mapper/blob - src/gtkmap.c
Map widget:
[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 "config.h"
21
22 #include <stdlib.h>
23 #include <glib/gstdio.h>
24 #include <glib-object.h>
25 #include <math.h>
26 #include <gtk/gtk.h>
27
28 #ifdef WITH_GL
29 #include <GL/gl.h>
30 #include <gtk/gtkgl.h>
31 #endif
32
33 #include "image-cache.h"
34 #include "map-repo.h"
35 #include "latlon.h"
36 #include "gtkmap.h"
37
38 #define MAP_THUMB_MARGIN_X (100)
39 #define MAP_THUMB_MARGIN_Y (75)
40
41 /* Initial size */
42 #define BUF_WIDTH_TILES (4)
43 #define BUF_HEIGHT_TILES (3)
44 #define BUF_WIDTH_PIXELS (1024)
45 #define BUF_HEIGHT_PIXELS (768)
46
47 /* Filename buffer */
48 #define BUFFER_SIZE (2048)
49
50 #define SCALE_WIDTH (256)
51
52 #define MAP_CACHE_DEFAULT (64)
53
54 G_DEFINE_TYPE(GtkMap, gtk_map, GTK_TYPE_WIDGET);
55
56 #define GTK_MAP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_MAP_TYPE, GtkMapPriv))
57
58 typedef struct _GtkMapPriv GtkMapPriv; 
59 struct _GtkMapPriv
60
61         /* Map image buffer */
62         GdkPixmap *buffer;
63         guint buf_width_tiles;
64         guint buf_height_tiles;
65         guint buf_width_pixels;
66         guint buf_height_pixels;
67
68         /* Cairo context for widget->window */
69 #ifdef WITH_CAIRO
70         cairo_t *ct;
71 #endif
72
73         PangoContext *context;
74         PangoLayout *layout;
75         PangoFontDescription *fontdesc;
76
77         PangoContext *speed_context;
78         PangoLayout *speed_layout;
79         PangoFontDescription *speed_font;
80
81         PangoContext *scale_context;
82         PangoLayout *scale_layout;
83         PangoFontDescription *scale_font;
84
85         GdkGC *gc_h;
86         GdkGC *gc_w;
87         GdkGC *gc_d;
88
89         GdkGC *speed_gc1;
90         GdkGC *speed_gc2;
91         GdkGC *speed_gc;
92
93         GdkRectangle scale_rect;
94
95         RepoData *curr_repo;
96
97         GTimer *timer;
98         ImageCache *icache;
99         GList *markers;
100
101         /* OpenGL data */
102 #ifdef WITH_GL
103         GdkGLConfig* gl_config;
104 #endif
105         gboolean gl;
106
107         GdkGC *gc_mark;
108         GdkGC *gc_velvec;
109
110         /* Cached Location dot x,y values */
111         gint mark_x1;
112         gint mark_x2;
113         gint mark_y1;
114         gint mark_y2;
115         GdkRectangle mark_rect;
116
117         GtkMapCenterMode center_mode;
118
119         Point center;
120         Point min_center;
121         Point max_center;
122         Point focus;
123
124         /* Our "location" on the map */
125         Point location;
126
127         guint lead_ratio;
128         guint center_ratio;
129
130         gint vel_offsetx;
131         gint vel_offsety;
132
133         guint base_tilex;
134         guint base_tiley;
135
136         /* Zoom settings */
137         gint zoom;
138         gint max_zoom;
139         gint min_zoom;
140         gboolean zoom_to_mouse;
141
142         gfloat units_conv;
143         gchar *units_str;
144
145         gfloat speed;
146
147         /* Buffer offset */
148         gint offsetx;
149         gint offsety;
150
151         guint screen_grids_halfwidth;
152         guint screen_grids_halfheight;
153         guint screen_width_pixels;
154         guint screen_height_pixels;
155
156         guint focus_unitwidth;
157         guint focus_unitheight;
158         guint world_size_tiles;
159
160         gint show_paths;
161         gboolean show_scale;
162         gboolean show_location;
163         gboolean show_velvec;
164         gboolean show_markers;
165
166         guint draw_width;
167
168         gboolean fast_render;
169
170         guint key_zoom_new;
171         guint key_zoom_timeout_sid;
172 };
173
174 #define tile2grid(tile) ((tile) << 3)
175 #define grid2tile(grid) ((grid) >> 3)
176 #define tile2pixel(tile) ((tile) << 8)
177 #define pixel2tile(pixel) ((pixel) >> 8)
178 #define tile2unit(tile) ((tile) << (8 + priv->zoom))
179 #define unit2tile(unit) ((unit) >> (8 + priv->zoom))
180 #define tile2zunit(tile, zoom) ((tile) << (8 + zoom))
181 #define unit2ztile(unit, zoom) ((unit) >> (8 + zoom))
182
183 #define grid2pixel(grid) ((grid) << 5)
184 #define pixel2grid(pixel) ((pixel) >> 5)
185 #define grid2unit(grid) ((grid) << (5 + priv->zoom))
186 #define unit2grid(unit) ((unit) >> (5 + priv->zoom))
187
188 #define pixel2unit(pixel) ((pixel) << priv->zoom)
189 #define unit2pixel(pixel) ((pixel) >> priv->zoom)
190 #define pixel2zunit(pixel, zoom) ((pixel) << (zoom))
191
192 #define unit2bufx(unit) (unit2pixel(unit) - tile2pixel(priv->base_tilex))
193 #define bufx2unit(x) (pixel2unit(x) + tile2unit(priv->base_tilex))
194 #define unit2bufy(unit) (unit2pixel(unit) - tile2pixel(priv->base_tiley))
195 #define bufy2unit(y) (pixel2unit(y) + tile2unit(priv->base_tiley))
196
197 #define unit2x(unit) (unit2pixel(unit) - tile2pixel(priv->base_tilex) - priv->offsetx)
198 #define x2unit(x) (pixel2unit(x + priv->offsetx) + tile2unit(priv->base_tilex))
199 #define unit2y(unit) (unit2pixel(unit) - tile2pixel(priv->base_tiley) - priv->offsety)
200 #define y2unit(y) (pixel2unit(y + priv->offsety) + tile2unit(priv->base_tiley))
201
202 #define leadx2unit (priv->location.unitx + (priv->lead_ratio) * pixel2unit(priv->vel_offsetx))
203 #define leady2unit (priv->location.unity + (0.6f*priv->lead_ratio) * pixel2unit(priv->vel_offsety))
204
205 #define GTK_MAP_TILE_SIZE_PIXELS (256)
206 #define GTK_MAP_TILE_SIZE_P2 (8)
207
208 /* #define GTK_MAP_WORLD_SIZE_UNITS(max_zoom) (2 << (max_zoom + GTK_MAP_TILE_SIZE_P2)) */
209
210 #define GTK_MAP_WORLD_SIZE_UNITS (2<<26)
211 #define WORLD_SIZE_UNITS GTK_MAP_WORLD_SIZE_UNITS
212
213 /* Pans are done two "grids" at a time, or 64 pixels. */
214 #define GTK_MAP_PAN_UNITS (grid2unit(2))
215
216 #define BOUND(x, a, b) { \
217         if((x) < (a)) \
218                 (x) = (a); \
219         else if((x) > (b)) \
220                 (x) = (b); \
221 }
222
223 static void gtk_map_finalize(GObject *object);
224
225 static void gtk_map_size_request(GtkWidget *widget, GtkRequisition *requisition);
226 static void gtk_map_size_allocate(GtkWidget *widget, GtkAllocation *allocate);
227 static void gtk_map_realize(GtkWidget *widget);
228 static gboolean gtk_map_expose(GtkWidget *widget, GdkEventExpose *event);
229 static gboolean gtk_map_configure(GtkWidget *widget, GdkEventConfigure *event);
230
231 static void gtk_map_scale_draw(GtkWidget *widget, GdkEventExpose *event);
232 static void gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event);
233
234 static gboolean gtk_map_update_buffer_size(GtkWidget *widget, gint new_width, gint new_height);
235 static void gtk_map_update_size(GtkWidget *widget, gint width, gint height);
236
237 static void gtk_map_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
238 static void gtk_map_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
239
240 static gboolean gtk_map_button_press_cb(GtkWidget *widget, GdkEventButton *event);
241 static gboolean gtk_map_button_release_cb(GtkWidget *widget, GdkEventButton *event);
242 static gboolean gtk_map_scroll_event_cb(GtkWidget *widget, GdkEventScroll *event); 
243
244 /* Signal IDs */
245 enum {
246         MAP_LOCATION_CHANGED,
247         MAP_ZOOM_CHANGED,
248         MAP_PANNED,
249         MAP_CENTER_MODE_CHANGED,
250         MARKER_CLICK,
251         LAST_SIGNAL
252 };
253
254 /* Property IDs */
255 enum {
256         LAST_PROP
257 };
258
259 static guint gtk_map_signals[LAST_SIGNAL] = { 0 };
260
261 static void
262 gtk_map_class_init(GtkMapClass *class)
263 {
264 GObjectClass *object_class;
265 GtkWidgetClass *widget_class;
266         
267 object_class = (GObjectClass*) class;
268 widget_class = (GtkWidgetClass*) class;
269         
270 object_class->finalize = gtk_map_finalize;
271 object_class->set_property = gtk_map_set_property;
272 object_class->get_property = gtk_map_get_property;
273         
274 widget_class->size_request = gtk_map_size_request;
275 widget_class->expose_event = gtk_map_expose;
276 widget_class->realize = gtk_map_realize;
277 widget_class->size_allocate = gtk_map_size_allocate;
278
279 g_type_class_add_private (object_class, sizeof(GtkMapPriv));
280
281 gtk_map_signals[MAP_ZOOM_CHANGED]=g_signal_new("zoom-changed", G_OBJECT_CLASS_TYPE(object_class),
282         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, zoom_changed),
283         NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
284
285 gtk_map_signals[MAP_LOCATION_CHANGED]=g_signal_new("location-changed", G_OBJECT_CLASS_TYPE(object_class),
286         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, location_changed),
287         NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
288
289 gtk_map_signals[MAP_LOCATION_CHANGED]=g_signal_new("center-mode-changed", G_OBJECT_CLASS_TYPE(object_class),
290         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, center_mode_changed),
291         NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
292
293 }
294
295 static inline void 
296 gtk_map_recalc_center(GtkMapPriv *priv)
297 {
298 g_return_if_fail(priv);
299 switch(priv->center_mode) {
300         case CENTER_LEAD:
301                 priv->center.unitx = leadx2unit;
302                 priv->center.unity = leady2unit;
303         break;
304         case CENTER_LATLON:
305                 priv->center.unitx = priv->location.unitx;
306                 priv->center.unity = priv->location.unity;
307         break;
308         case CENTER_MANUAL:
309         default:
310                 /* Do nothing, use priv->center.* */
311         break;
312 }
313 }
314
315 static inline void
316 gtk_map_recalc_offset(GtkMapPriv *priv)
317 {
318 g_return_if_fail(priv);
319 priv->offsetx = grid2pixel(unit2grid(priv->center.unitx) - priv->screen_grids_halfwidth - tile2grid(priv->base_tilex));
320 priv->offsety = grid2pixel(unit2grid(priv->center.unity) - priv->screen_grids_halfheight - tile2grid(priv->base_tiley));
321 }
322
323 static inline void 
324 gtk_map_recalc_focus_base(GtkMapPriv *priv)
325 {
326 g_return_if_fail(priv);
327 priv->focus.unitx = x2unit(priv->screen_width_pixels * priv->center_ratio / 20);
328 priv->focus.unity = y2unit(priv->screen_height_pixels * priv->center_ratio / 20);
329 }
330
331 static inline void
332 gtk_map_recalc_focus_size(GtkMapPriv *priv)
333 {
334 g_return_if_fail(priv);
335 priv->focus_unitwidth = pixel2unit((10 - priv->center_ratio) * priv->screen_width_pixels / 10);
336 priv->focus_unitheight = pixel2unit((10 - priv->center_ratio) * priv->screen_height_pixels / 10);
337 }
338
339 static inline void 
340 gtk_map_recalc_center_bounds(GtkMapPriv *priv)
341 {
342 g_return_if_fail(priv);
343 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
344 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
345 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfwidth) - 1;
346 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfheight) - 1;
347 }
348
349 static void
350 gtk_map_init(GtkMap *map)
351 {
352 GtkMapPriv *priv;
353
354 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
355 priv=GTK_MAP_GET_PRIVATE(map);
356
357 priv->base_tilex=-5;
358 priv->base_tiley=-5;
359
360 priv->draw_width=4;
361
362 priv->zoom=3;
363 priv->min_zoom=0;
364 priv->max_zoom=17;
365 priv->zoom_to_mouse=TRUE;
366
367 priv->center_mode=CENTER_LATLON;
368 priv->center.unitx=0;
369 priv->center.unity=0;
370
371 priv->speed=-1;
372 priv->speed_gc=priv->speed_gc1;
373
374 priv->units_conv=1.85200;
375
376 priv->icache=image_cache_new(64);
377
378 priv->show_scale=TRUE;
379 priv->show_velvec=TRUE;
380 priv->show_markers=TRUE;
381 priv->show_location=TRUE;
382
383 priv->gl=FALSE;
384 priv->buffer=NULL;
385 }
386
387 static void
388 gtk_map_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
389 {
390 GtkMap *map;
391 g_return_if_fail(GTK_IS_MAP(object));
392 map=GTK_MAP(object);
393 switch (prop_id) {
394         default:
395                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
396         break;
397 }
398 }
399
400 static void
401 gtk_map_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
402 {
403 GtkMap *map;
404
405 g_return_if_fail(GTK_IS_MAP(object));
406 map=GTK_MAP(object);
407 switch (prop_id) {
408         default:
409                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
410         break;
411 }
412 }
413
414 GtkWidget*
415 gtk_map_new(void)
416 {
417 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
418 return g_object_new(GTK_MAP_TYPE, NULL);
419 }
420
421 static void
422 gtk_map_finalize(GObject *object)
423 {
424 GtkMap *map;
425 GtkMapPriv *priv;
426         
427 g_return_if_fail(GTK_IS_MAP(object));
428 map=GTK_MAP(object);
429 priv=GTK_MAP_GET_PRIVATE(map);
430
431 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
432
433 #ifdef WITH_CAIRO
434 if (priv->ct)
435         cairo_destroy(priv->ct);
436 #endif
437
438 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
439         gtk_widget_unmap(GTK_WIDGET(object));
440 }
441
442 G_OBJECT_CLASS(gtk_map_parent_class)->finalize(object);
443 }
444
445 static void
446 gtk_map_update_size(GtkWidget *widget, gint width, gint height)
447 {
448 GtkMap *map;
449 GtkMapPriv *priv;
450 guint tw, th;
451
452 g_return_if_fail(GTK_IS_MAP(widget));
453 map=GTK_MAP(widget);
454 priv=GTK_MAP_GET_PRIVATE(map);
455
456 tw=GTK_MAP_TILE_SIZE_PIXELS*((width/GTK_MAP_TILE_SIZE_PIXELS)+2);
457 th=GTK_MAP_TILE_SIZE_PIXELS*((height/GTK_MAP_TILE_SIZE_PIXELS)+2);
458
459 gtk_map_update_buffer_size(GTK_MAP(widget), tw, th);
460 if (!priv->buffer) {
461         return;
462 }
463
464 priv->screen_width_pixels = width;
465 priv->screen_height_pixels = height;
466 priv->screen_grids_halfwidth = pixel2grid(priv->screen_width_pixels) / 2;
467 priv->screen_grids_halfheight = pixel2grid(priv->screen_height_pixels) / 2;
468
469 /* Set scale_rect. */
470 priv->scale_rect.x = (priv->screen_width_pixels - SCALE_WIDTH) / 2;
471 priv->scale_rect.width = SCALE_WIDTH;
472
473 gtk_map_recalc_focus_base(priv);
474 gtk_map_recalc_focus_size(priv);
475
476 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
477 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
478 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfwidth) - 1;
479 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfheight) - 1;
480 }
481
482 static void
483 gtk_map_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
484 {
485 GtkMap *map;
486 GtkMapPriv *priv;
487
488 g_return_if_fail(GTK_IS_MAP(widget));
489 map=GTK_MAP(widget);
490 priv=GTK_MAP_GET_PRIVATE(map);
491 widget->allocation = *allocation;
492
493 g_debug("GTKMAP: Size allocate (%d, %d)", widget->allocation.width, widget->allocation.height);
494
495 if (GTK_WIDGET_REALIZED(widget)) {
496         gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
497         gtk_map_update_size(widget, allocation->width, allocation->height);
498         gtk_map_refresh(widget);
499 }
500
501 }
502
503 static void
504 gtk_map_size_request(GtkWidget  *widget, GtkRequisition *requisition)
505 {
506 GtkMap *map;
507         
508 g_return_if_fail(GTK_IS_MAP(widget));
509 g_return_if_fail(requisition != NULL);
510         
511 map=GTK_MAP(widget);
512
513 g_debug("GTKMAP: Size request");
514
515 requisition->width=GTK_MAP_TILE_SIZE_PIXELS/2;
516 requisition->height=GTK_MAP_TILE_SIZE_PIXELS/2;
517 /* gtk_map_update_size(widget, requisition->width, requisition->height); */
518 }
519
520 static gboolean
521 gtk_map_update_buffer_size(GtkWidget *widget, gint new_width, gint new_height)
522 {
523 GtkMap *map;
524 GtkMapPriv *priv;
525
526 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
527 g_return_val_if_fail(widget->window, FALSE);
528
529 map=GTK_MAP(widget);
530 priv=GTK_MAP_GET_PRIVATE(map);
531
532 g_debug("GTKMAP: %s (%d, %d)", __PRETTY_FUNCTION__, new_width, new_height);
533
534 if ( (priv->buffer==NULL) || (new_width>priv->buf_width_pixels || new_height>priv->buf_height_pixels || 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         priv->buf_width_pixels=new_width<GTK_MAP_TILE_SIZE_PIXELS ? GTK_MAP_TILE_SIZE_PIXELS : new_width;
536         priv->buf_height_pixels=new_height<GTK_MAP_TILE_SIZE_PIXELS ? GTK_MAP_TILE_SIZE_PIXELS : new_height;
537         priv->buf_width_tiles=priv->buf_width_pixels/GTK_MAP_TILE_SIZE_PIXELS;
538         priv->buf_height_tiles=priv->buf_height_pixels/GTK_MAP_TILE_SIZE_PIXELS;
539         if (priv->buffer)
540                 g_object_unref(priv->buffer);
541         priv->buffer=gdk_pixmap_new(widget->window, priv->buf_width_pixels, priv->buf_height_pixels, -1);
542         if (priv->buffer)
543                 gdk_draw_rectangle(priv->buffer, widget->style->black_gc, TRUE, 0, 0, priv->buf_width_pixels, priv->buf_height_pixels);
544         return TRUE;
545 }
546 return FALSE;
547 }
548
549 static void 
550 gtk_map_realize(GtkWidget *widget)
551 {
552 GtkMap *map;
553 GtkMapPriv *priv;
554 GdkColor color;
555 GdkWindowAttr attributes;
556 guint attributes_mask;
557
558 g_return_if_fail(GTK_IS_MAP(widget));
559 map=GTK_MAP(widget);
560 priv=GTK_MAP_GET_PRIVATE(map);
561
562 g_debug("GTKMAP: Realize");
563
564 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
565 attributes.window_type=GDK_WINDOW_CHILD;
566 attributes.x=widget->allocation.x;
567 attributes.y=widget->allocation.y;
568 attributes.width=widget->allocation.width;
569 attributes.height=widget->allocation.height;
570
571 attributes.wclass=GDK_INPUT_OUTPUT;
572 attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
573 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
574
575 attributes.visual=gtk_widget_get_visual(widget);
576 attributes.colormap=gtk_widget_get_colormap(widget);
577
578 widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
579
580 gdk_window_set_user_data(widget->window, widget);
581
582 widget->style=gtk_style_attach(widget->style, widget->window);
583 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
584
585 gtk_widget_set_extension_events(widget, GDK_EXTENSION_EVENTS_ALL);
586
587 #ifdef WITH_GL
588 priv->gl_config=gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
589 if (priv->gl_config) {
590     g_print("OpenGL version: %s\n", glGetString (GL_VERSION));
591     g_print("OpenGL vendor: %s\n", glGetString (GL_VENDOR));
592     g_print("OpenGL renderer: %s\n", glGetString (GL_RENDERER));
593         gtk_widget_set_gl_capability(widget->window, priv->gl_config, NULL, TRUE, GDK_GL_RGBA_TYPE);
594         priv->gl=TRUE;
595 }
596 #endif
597
598 #if 0
599 g_signal_connect(G_OBJECT(map), "button_press_event", G_CALLBACK(map_cb_button_press), NULL);
600 g_signal_connect(G_OBJECT(map), "button_release_event",G_CALLBACK(map_cb_button_release), NULL);
601 #endif
602
603 g_signal_connect(G_OBJECT(map), "scroll_event",  G_CALLBACK(gtk_map_scroll_event_cb), NULL);
604
605 priv->scale_context=gtk_widget_get_pango_context(widget);
606 priv->scale_layout=pango_layout_new(priv->scale_context);
607 priv->scale_font=pango_font_description_new();
608 pango_font_description_set_size(priv->scale_font, 12 * PANGO_SCALE);
609 pango_layout_set_font_description(priv->scale_layout, priv->scale_font);
610
611 /* Speed limit, over limit color */
612 priv->speed_gc1=gdk_gc_new(widget->window);
613 color.red=0xffff;
614 color.green=0;
615 color.blue=0;
616 gdk_gc_set_rgb_fg_color(priv->speed_gc1, &color);
617
618 /* Speed limit, under limit color */
619 priv->speed_gc2=gdk_gc_new(widget->window);
620 color.red=0;
621 color.green=0x1000;
622 color.blue=0;
623 gdk_gc_set_rgb_fg_color(priv->speed_gc2, &color);
624
625 priv->speed_context=gtk_widget_get_pango_context(widget);
626 priv->speed_layout=pango_layout_new(priv->speed_context);
627 priv->speed_font=pango_font_description_new();
628 pango_font_description_set_size(priv->speed_font, 48 * PANGO_SCALE);
629 pango_layout_set_font_description(priv->speed_layout, priv->speed_font);
630 pango_layout_set_alignment(priv->speed_layout, PANGO_ALIGN_LEFT);
631
632 #if 0
633 g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(gtk_map_press_cb), NULL);
634 #endif
635
636 gtk_widget_queue_resize(widget);
637 }
638
639 static gboolean
640 gtk_map_expose(GtkWidget *widget, GdkEventExpose *event)
641 {
642 GtkMap *map;
643 GtkMapPriv *priv;
644
645 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
646 g_return_val_if_fail(event != NULL, FALSE);
647
648 map=GTK_MAP(widget);
649 priv=GTK_MAP_GET_PRIVATE(map);
650
651 #ifdef WITH_CAIRO
652 priv->ct=gdk_cairo_create(widget->window);
653 #endif
654
655 g_debug("GTKMAP: expose (%d, %d)-(%d, %d)", event->area.x, event->area.y, event->area.width, event->area.height);
656
657 if (priv->buffer)
658         gdk_draw_drawable(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL], priv->buffer,
659                 event->area.x + priv->offsetx, event->area.y + priv->offsety,
660                 event->area.x, event->area.y, event->area.width, event->area.height);
661
662 #if 0
663 gtk_map_paths_draw(widget, event);
664
665 if (priv->show_markers)
666         gtk_map_markers_draw(widget, event);
667
668 if (priv->show_speed)
669         gtk_map_speed_draw(widget, event);
670 #endif
671
672 if (priv->show_location)
673         gtk_map_mark_draw(widget, event);
674
675 if (priv->show_scale)
676         gtk_map_scale_draw(widget, event);
677
678 #ifdef WITH_CAIRO
679 cairo_destroy(priv->ct);
680 priv->ct=NULL;
681 #endif
682
683 return TRUE;
684 }
685
686 static void 
687 gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event)
688 {
689 GtkMap *map;
690 GtkMapPriv *priv;
691
692 map=GTK_MAP(widget);
693 priv=GTK_MAP_GET_PRIVATE(map);
694
695 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
696
697 if (!gdk_rectangle_intersect(&event->area, &priv->mark_rect, NULL))
698         return;
699
700 return;
701
702 #ifdef WITH_CAIRO
703 cairo_save(priv->ct);
704 cairo_arc(priv->ct, priv->mark_x1, priv->mark_y1, priv->draw_width*2, 0, 2 * M_PI);
705 cairo_set_source_rgb(priv->ct, 1, 1, 1);
706 cairo_fill_preserve(priv->ct);
707 cairo_set_source_rgba(priv->ct, 0.2, 0.2, 0.8, 0.7);
708 if (priv->show_velvec) {
709         cairo_save(priv->ct);
710         cairo_set_line_width(priv->ct, priv->draw_width);
711         cairo_move_to(priv->ct, priv->mark_x1, priv->mark_y1);
712         cairo_line_to(priv->ct, priv->mark_x2, priv->mark_y2);
713         cairo_restore(priv->ct);
714 }
715 cairo_restore(priv->ct);
716 cairo_stroke(priv->ct);
717 #else
718 gdk_draw_arc(widget->window, priv->gc_mark, FALSE,
719         priv->mark_x1 - priv->draw_width,
720         priv->mark_y1 - priv->draw_width,
721         2 * priv->draw_width, 2 * priv->draw_width,
722         0, 360 * 64);
723
724 if (priv->show_velvec)
725         gdk_draw_line(widget->window, priv->gc_velvec,
726                 priv->mark_x1, priv->mark_y1, 
727                 priv->mark_x2, priv->mark_y2);
728 #endif
729 }
730
731 static void 
732 gtk_map_calculate_mark(GtkWidget *widget)
733 {
734 GtkMap *map;
735 GtkMapPriv *priv;
736
737 map=GTK_MAP(widget);
738 priv=GTK_MAP_GET_PRIVATE(map);
739
740 priv->mark_x1 = unit2x(priv->location.unitx);
741 priv->mark_y1 = unit2y(priv->location.unity);
742 priv->mark_x2 = priv->mark_x1 + (priv->show_velvec ? priv->vel_offsetx : 0);
743 priv->mark_y2 = priv->mark_y1 + (priv->show_velvec ? priv->vel_offsety : 0);
744
745 priv->mark_rect.x = MIN(priv->mark_x1, priv->mark_x2) - (2 * priv->draw_width);
746 priv->mark_rect.y = MIN(priv->mark_y1, priv->mark_y2) - (2 * priv->draw_width);
747 priv->mark_rect.width = abs(priv->mark_x1 - priv->mark_x2) + (4 * priv->draw_width);
748 priv->mark_rect.height = abs(priv->mark_y1 - priv->mark_y2) + (4 * priv->draw_width);
749 }
750
751 #define SCALE_PADDING (4)
752
753 static void 
754 gtk_map_scale_draw(GtkWidget *widget, GdkEventExpose *event)
755 {
756 GtkMap *map;
757 GtkMapPriv *priv;
758 gchar buffer[16];
759 gdouble distance;
760 gdouble lat1, lon1, lat2, lon2;
761 gint width;
762
763 g_return_if_fail(GTK_IS_MAP(widget));
764 map=GTK_MAP(widget);
765 priv=GTK_MAP_GET_PRIVATE(map);
766
767 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
768
769 pango_layout_set_text(priv->scale_layout, "0", -1);
770 pango_layout_get_pixel_size(priv->scale_layout, NULL, &priv->scale_rect.height);
771 priv->scale_rect.y = priv->screen_height_pixels - priv->scale_rect.height - 1;
772
773 if (!gdk_rectangle_intersect(&event->area, &priv->scale_rect, NULL))
774         return;
775
776 /* Now calculate and draw the distance. */
777 unit2latlon(priv->center.unitx - pixel2unit(SCALE_WIDTH / 2 - SCALE_PADDING), priv->center.unity, lat1, lon1);
778 unit2latlon(priv->center.unitx + pixel2unit(SCALE_WIDTH / 2 - SCALE_PADDING), priv->center.unity, lat2, lon2);
779 distance=calculate_distance(lat1, lon1, lat2, lon2) * priv->units_conv;
780
781 if (distance < 1.f)
782         g_snprintf(buffer, sizeof(buffer), "%0.2f %s", distance, priv->units_str);
783 else if (distance < 10.f)
784         g_snprintf(buffer, sizeof(buffer), "%0.1f %s", distance, priv->units_str);
785 else
786         g_snprintf(buffer, sizeof(buffer), "%0.f %s", distance, priv->units_str);
787
788 g_debug("SCALE: %s", buffer);
789
790 #ifdef WITH_CAIRO
791 cairo_rectangle(priv->ct, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
792 cairo_set_source_rgba(priv->ct, 1, 1, 1, 0.65);
793 cairo_fill_preserve(priv->ct);
794 cairo_set_source_rgba(priv->ct, 0, 0, 0, 0.81);
795 #else
796 gdk_draw_rectangle(widget->window, widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
797            TRUE, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
798 gdk_draw_rectangle(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
799            FALSE, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
800 #endif
801
802 pango_layout_set_text(priv->scale_layout, buffer, -1);
803 pango_layout_get_pixel_size(priv->scale_layout, &width, NULL);
804
805 /* Draw little hashes on the ends. */
806 #ifdef WITH_CAIRO
807 cairo_set_source_rgb(priv->ct, 0, 0, 0);
808 cairo_move_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING);
809 cairo_line_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
810
811 cairo_move_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
812 cairo_line_to(priv->ct, priv->scale_rect.x + (priv->scale_rect.width - width) / 2 - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
813
814 cairo_move_to(priv->ct, priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING);
815 cairo_line_to(priv->ct, priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
816
817 cairo_move_to(priv->ct, priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
818 cairo_line_to(priv->ct, priv->scale_rect.x + (priv->scale_rect.width + width) / 2 + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
819
820 cairo_stroke(priv->ct);
821 #else
822 gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
823             priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING,
824             priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
825 gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
826                 priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2,
827             priv->scale_rect.x + (priv->scale_rect.width - width) / 2 - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
828 gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
829             priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING,
830             priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
831 gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
832             priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2,
833             priv->scale_rect.x + (priv->scale_rect.width + width) / 2 + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
834 #endif
835
836 /* Draw the layout itself. */
837 gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
838                 priv->scale_rect.x + (priv->scale_rect.width - width) / 2, priv->scale_rect.y, priv->scale_layout);
839
840 }
841
842 static void
843 gtk_map_information_text(GtkWidget *widget, guint x, guint y, GdkGC *gc, gchar *msg)
844 {
845 GtkMap *map;
846 GtkMapPriv *priv;
847 guint width, height;
848
849 g_return_if_fail(GTK_IS_MAP(widget));
850 map=GTK_MAP(widget);
851 priv=GTK_MAP_GET_PRIVATE(map);
852
853 pango_layout_set_text(priv->speed_layout, msg, -1);
854 pango_layout_get_pixel_size(priv->speed_layout, &width, &height);
855 gtk_widget_queue_draw_area(widget, x - 5, y - 5, width * 3 + 15, height + 5);
856 gdk_window_process_all_updates();
857 gdk_draw_layout(widget->window, gc, x, y, priv->speed_layout);
858 gdk_window_process_all_updates();
859 }
860
861 static void
862 gtk_map_speed_draw(GtkWidget *widget, GdkEventExpose *event)
863 {
864 GtkMap *map;
865 GtkMapPriv *priv;
866 gchar buffer[16];
867
868 g_return_if_fail(GTK_IS_MAP(widget));
869 map=GTK_MAP(widget);
870 priv=GTK_MAP_GET_PRIVATE(map);
871
872 if (priv->speed<0)
873         return;
874
875 g_snprintf(buffer, sizeof(buffer), "%0.0f %s", priv->speed * priv->units_conv, priv->units_str);
876 map_information_text(10, 10, priv->speed_gc, buffer);
877 }
878
879 void
880 gtk_map_set_speed(GtkWidget *widget, gfloat speed, gboolean overspeed)
881 {
882 GtkMap *map;
883 GtkMapPriv *priv;
884
885 g_return_if_fail(GTK_IS_MAP(widget));
886 map=GTK_MAP(widget);
887 priv=GTK_MAP_GET_PRIVATE(map);
888
889 priv->speed_gc=(overspeed) ? priv->speed_gc1 : priv->speed_gc2;
890 priv->speed=speed;
891 }
892
893 /**
894  * Do an in-place scaling of a pixbuf's pixels at the given ratio from the given source location.
895  */
896 static void
897 gtk_map_pixbuf_scale_inplace(GdkPixbuf *pixbuf, guint ratio_p2, guint src_x, guint src_y)
898 {
899 guint dest_x = 0, dest_y = 0, dest_dim = GTK_MAP_TILE_SIZE_PIXELS;
900 guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
901 guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
902 guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
903
904 /* Sweep through the entire dest area, copying as necessary, but
905  * DO NOT OVERWRITE THE SOURCE AREA.  We'll copy it afterward. */
906 do {
907         guint src_dim = dest_dim >> ratio_p2;
908         guint src_endx = src_x - dest_x + src_dim;
909         gint x, y;
910
911         for (y = dest_dim - 1; y >= 0; y--) {
912                 guint src_offset_y, dest_offset_y;
913
914                 src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
915                 dest_offset_y = (dest_y + y) * rowstride;
916                 x = dest_dim - 1;
917
918                 if ((unsigned)(dest_y + y - src_y) < src_dim && (unsigned)(dest_x + x - src_x) < src_dim)
919                         x -= src_dim;
920
921                 for (; x >= 0; x--) {
922                         guint src_offset, dest_offset, i;
923
924                         src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
925                         dest_offset = dest_offset_y + (dest_x + x) * n_channels;
926
927                         pixels[dest_offset] = pixels[src_offset];
928                         for (i = n_channels - 1; i; i--)
929                                 pixels[dest_offset + i] = pixels[src_offset + i];
930
931                         if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
932                                 x -= src_dim;
933                 }
934         }
935
936         /* Reuse src_dim and src_endx to store new src_x and src_y. */
937         src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
938         src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
939         dest_x = src_x;
940         dest_y = src_y;
941         src_x = src_dim;
942         src_y = src_endx;
943 }
944 while ((dest_dim >>= ratio_p2) > 1);
945 }
946
947 static GdkPixbuf *
948 gtk_map_tile_load(GtkWidget *widget, guint tilex, guint tiley, gint zoff, gboolean fast_fail)
949 {
950 GtkMap *map;
951 GtkMapPriv *priv;
952 GdkPixbuf *pixbuf;
953 GError *error = NULL;
954 gchar buffer[BUFFER_SIZE];
955 gchar key[BUFFER_SIZE];
956 struct stat tstat;
957 gint se;
958
959 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
960 map=GTK_MAP(widget);
961 priv=GTK_MAP_GET_PRIVATE(map);
962
963 g_snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg", priv->curr_repo->cache_dir, priv->zoom + zoff, (tilex >> zoff), (tiley >> zoff));
964 g_snprintf(key, sizeof(key), "%s/%u/%u/%u", priv->curr_repo->cache_dir, priv->zoom + zoff, (tilex >> zoff), (tiley >> zoff));
965
966 g_debug("LOAD: %u, %u @ (%d+%d): %s", tilex, tiley, priv->zoom, zoff, buffer);
967
968 pixbuf=image_cache_get(priv->icache, key, buffer);
969 if (!pixbuf) {
970 #if 0
971         g_unlink(buffer);
972
973         if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
974                 if (download)
975                         map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
976         }
977 #endif
978         return NULL;
979 }
980
981 g_object_ref(pixbuf);
982
983 #if 0
984 /* Check if we need to trim. */
985 if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
986         pixbuf=pixbuf_trim(pixbuf);
987 #endif
988
989 #if 0
990 /* Check tile age, if file date is ower a week old, redownload if autodownload enabled */
991 se=stat(buffer, &tstat);
992 if (se==0) {
993         time_t t;
994         t=time(NULL);
995         if (t-tstat.st_mtime>TILE_MAX_AGE) {
996                 if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
997                         if (download) {
998                                 g_debug("Tile: %s is old, re-downloading\n", buffer);
999                                 map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
1000                                 image_cache_invalidate(map_ic, key);
1001                         }
1002                 }
1003         }
1004 }
1005 #endif
1006
1007 return pixbuf;
1008 }
1009
1010 void 
1011 gtk_map_set_tile_repository(GtkWidget *widget, RepoData *rd)
1012 {
1013 GtkMap *map;
1014 GtkMapPriv *priv;
1015
1016 g_return_if_fail(GTK_IS_MAP(widget));
1017 map=GTK_MAP(widget);
1018 priv=GTK_MAP_GET_PRIVATE(map);
1019
1020 priv->curr_repo=rd;
1021 }
1022
1023 static gboolean
1024 gtk_map_render_tile(GtkWidget *widget, guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
1025 {
1026 GtkMap *map;
1027 GtkMapPriv *priv;
1028 GdkPixbuf *pixbuf=NULL;
1029 gint zoff;
1030
1031 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1032 map=GTK_MAP(widget);
1033 priv=GTK_MAP_GET_PRIVATE(map);
1034
1035 g_debug("RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, 
1036         priv->world_size_tiles, destx, desty, priv->buf_width_tiles, priv->buf_height_tiles);
1037
1038 g_return_val_if_fail(priv->buffer, FALSE);
1039 g_return_val_if_fail(priv->curr_repo, FALSE);
1040
1041 if (destx > priv->buf_width_pixels || desty > priv->buf_height_pixels)
1042         return FALSE;
1043
1044 if (tilex > priv->world_size_tiles || tiley > priv->world_size_tiles)
1045         return FALSE;
1046
1047 for (zoff = (priv->curr_repo->double_size ? 1 : 0); !pixbuf && (priv->zoom + zoff) <= priv->max_zoom && zoff <= GTK_MAP_TILE_SIZE_P2; zoff += 1) {
1048         pixbuf=gtk_map_tile_load(map, tilex, tiley, zoff, !fast_fail);
1049         if (!pixbuf) {
1050                 if (!fast_fail)
1051                         fast_fail=TRUE;
1052         } else {
1053                 /* Check if we need to blit. */
1054                 if (zoff) {
1055                         gtk_map_pixbuf_scale_inplace(pixbuf, zoff,
1056                                 (tilex - ((tilex >> zoff) << zoff)) << (GTK_MAP_TILE_SIZE_P2 - zoff),
1057                                 (tiley - ((tiley >> zoff) << zoff)) << (GTK_MAP_TILE_SIZE_P2 - zoff));
1058                         image_cache_invalidate_by_image(priv->icache, pixbuf);
1059                 }
1060         }
1061 }
1062
1063 if (pixbuf) {
1064         gdk_draw_pixbuf(priv->buffer, 
1065             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1066                 pixbuf, 0, 0, destx, desty, 
1067                 GTK_MAP_TILE_SIZE_PIXELS, 
1068                 GTK_MAP_TILE_SIZE_PIXELS, 
1069                 GDK_RGB_DITHER_NONE, 0, 0);
1070         g_object_unref(pixbuf);
1071         return TRUE;
1072 }
1073 gdk_draw_rectangle(priv->buffer, 
1074         widget->style->black_gc, TRUE, 
1075         destx, desty, 
1076         GTK_MAP_TILE_SIZE_PIXELS, 
1077         GTK_MAP_TILE_SIZE_PIXELS);
1078 return TRUE;
1079 }
1080
1081 gboolean
1082 gtk_map_set_center(GtkWidget *widget, guint unitx, guint unity)
1083 {
1084 GtkMap *map;
1085 GtkMapPriv *priv;
1086 GtkStyle *style;
1087 gint new_base_tilex, new_base_tiley;
1088 guint new_x, new_y;
1089 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
1090
1091 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1092
1093 map=GTK_MAP(widget);
1094 priv=GTK_MAP_GET_PRIVATE(map);
1095 style=widget->style;
1096
1097 priv->center_mode=CENTER_MANUAL;
1098
1099 /* Assure that _center.unitx/y are bounded. */
1100 BOUND(unitx, priv->min_center.unitx, priv->max_center.unitx);
1101 BOUND(unity, priv->min_center.unity, priv->max_center.unity);
1102
1103 priv->center.unitx = unitx;
1104 priv->center.unity = unity;
1105
1106 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) priv->center.unitx)) - (gint)priv->screen_grids_halfwidth);
1107 new_base_tiley = grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
1108
1109 /* Same zoom level, so it's likely that we can reuse some of the old buffer's pixels. */
1110
1111 if (new_base_tilex != priv->base_tilex || new_base_tiley != priv->base_tiley) {
1112         /* If copying from old parts to new parts, we need to make sure we
1113          * don't overwrite the old parts when copying, so set up new_x,
1114          * new_y, old_x, old_y, iox, and ioy with that in mind. */
1115         if (new_base_tiley < priv->base_tiley) {
1116                 /* New is lower than old - start at bottom and go up. */
1117                 new_y = priv->buf_height_tiles - 1;
1118                 ioy = -1;
1119         } else {
1120                 /* New is higher than old - start at top and go down. */
1121                 new_y = 0;
1122                 ioy = 1;
1123         }
1124         if (new_base_tilex < priv->base_tilex) {
1125                 /* New is righter than old - start at right and go left. */
1126                 base_new_x = priv->buf_width_tiles - 1;
1127                 iox = -1;
1128         } else {
1129                 /* New is lefter than old - start at left and go right. */
1130                 base_new_x = 0;
1131                 iox = 1;
1132         }
1133
1134         /* Iterate over the y tile values. */
1135         old_y = new_y + new_base_tiley - priv->base_tiley;
1136         base_old_x = base_new_x + new_base_tilex - priv->base_tilex;
1137         priv->base_tilex = new_base_tilex;
1138         priv->base_tiley = new_base_tiley;
1139
1140         for (j = 0; j < priv->buf_height_tiles; ++j, new_y += ioy, old_y += ioy) {
1141                 new_x = base_new_x;
1142                 old_x = base_old_x;
1143                 /* Iterate over the x tile values. */
1144                 for (k = 0; k < priv->buf_width_tiles; ++k, new_x += iox, old_x += iox) {
1145                         /* Can we get this grid block from the old buffer?. */
1146                         if (old_x >= 0 && old_x < priv->buf_width_tiles && old_y >= 0 && old_y < priv->buf_height_tiles) {
1147                                 /* Copy from old buffer to new buffer. */
1148                                 gdk_draw_drawable(priv->buffer,
1149                                             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1150                                                 priv->buffer,
1151                                                 old_x * GTK_MAP_TILE_SIZE_PIXELS,
1152                                                 old_y * GTK_MAP_TILE_SIZE_PIXELS,
1153                                                 new_x * GTK_MAP_TILE_SIZE_PIXELS,
1154                                                 new_y * GTK_MAP_TILE_SIZE_PIXELS,
1155                                                 GTK_MAP_TILE_SIZE_PIXELS,
1156                                                 GTK_MAP_TILE_SIZE_PIXELS);
1157                         } else {
1158                                 gtk_map_render_tile(GTK_MAP(map), 
1159                                                 new_base_tilex + new_x,
1160                                                 new_base_tiley + new_y,
1161                                                 new_x * GTK_MAP_TILE_SIZE_PIXELS,
1162                                                 new_y * GTK_MAP_TILE_SIZE_PIXELS,
1163                                                 priv->fast_render);
1164                         }
1165                 }
1166         }
1167 }
1168
1169 gtk_map_recalc_offset(priv);
1170 gtk_map_recalc_focus_base(priv);
1171 gtk_map_refresh(widget);
1172 g_signal_emit(widget, gtk_map_signals[MAP_LOCATION_CHANGED], 0, priv->zoom);
1173 }
1174
1175 void
1176 gtk_map_pan(GtkWidget *widget, gint delta_x, gint delta_y)
1177 {
1178 GtkMap *map;
1179 GtkMapPriv *priv;
1180
1181 g_return_if_fail(GTK_IS_MAP(widget));
1182 map=GTK_MAP(widget);
1183 priv=GTK_MAP_GET_PRIVATE(map);
1184
1185 gtk_map_set_center(widget, priv->center.unitx + delta_x*GTK_MAP_PAN_UNITS, priv->center.unity + delta_y*GTK_MAP_PAN_UNITS);
1186 }
1187
1188 static void
1189 gtk_map_refresh(GtkWidget *widget)
1190 {
1191 GtkMap *map;
1192 GtkMapPriv *priv;
1193 guint x, y;
1194
1195 g_return_if_fail(GTK_IS_MAP(widget));
1196 map=GTK_MAP(widget);
1197 priv=GTK_MAP_GET_PRIVATE(map);
1198
1199 for (y = 0; y < priv->buf_height_tiles; ++y)
1200         for (x = 0; x < priv->buf_width_tiles; ++x)
1201                 gtk_map_render_tile(map, priv->base_tilex + x, priv->base_tiley + y, x * GTK_MAP_TILE_SIZE_PIXELS, y * GTK_MAP_TILE_SIZE_PIXELS, FALSE);
1202
1203 gtk_widget_queue_draw(widget);
1204 }
1205
1206 gint 
1207 gtk_map_get_zoom(GtkWidget *widget)
1208 {
1209 GtkMap *map;
1210 GtkMapPriv *priv;
1211
1212 g_return_val_if_fail(GTK_IS_MAP(widget), -1);
1213
1214 map=GTK_MAP(widget);
1215 priv=GTK_MAP_GET_PRIVATE(map);
1216
1217 return priv->zoom;
1218 }
1219
1220 gboolean
1221 gtk_map_set_zoom(GtkWidget *widget, gint new_zoom)
1222 {
1223 GtkMap *map;
1224 GtkMapPriv *priv;
1225
1226 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1227
1228 map=GTK_MAP(widget);
1229 priv=GTK_MAP_GET_PRIVATE(map);
1230
1231 g_debug("GTKMAP: zoom %d", new_zoom);
1232
1233 if (new_zoom > (priv->max_zoom - 1))
1234         return FALSE;
1235
1236 if (new_zoom == priv->zoom)
1237         return FALSE;
1238
1239 if (priv->curr_repo)
1240         priv->zoom = new_zoom / priv->curr_repo->view_zoom_steps * priv->curr_repo->view_zoom_steps;
1241 else
1242         priv->zoom = new_zoom;
1243
1244 priv->world_size_tiles = unit2tile(GTK_MAP_WORLD_SIZE_UNITS);
1245
1246 /* If we're leading, update the center to reflect new zoom level. */
1247 gtk_map_recalc_center(priv);
1248
1249 /* Update center bounds to reflect new zoom level. */
1250 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
1251 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
1252 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfwidth) - 1;
1253 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfheight) - 1;
1254
1255 BOUND(priv->center.unitx, priv->min_center.unitx, priv->max_center.unitx);
1256 BOUND(priv->center.unity, priv->min_center.unity, priv->max_center.unity);
1257
1258 priv->base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) priv->center.unitx)) - (gint) priv->screen_grids_halfwidth);
1259 priv->base_tiley = grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
1260
1261 /* New zoom level, so we can't reuse the old buffer's pixels. Update state variables. */
1262 gtk_map_recalc_offset(priv);
1263 gtk_map_recalc_focus_base(priv);
1264 gtk_map_recalc_focus_size(priv);
1265
1266 gtk_map_refresh(widget);
1267 g_signal_emit(widget, gtk_map_signals[MAP_ZOOM_CHANGED], 0, priv->zoom);
1268
1269 return TRUE;
1270 }
1271
1272 gint 
1273 gtk_map_zoom(GtkWidget *widget, gint zdir)
1274 {
1275 gint nzoom;
1276 GtkMap *map;
1277 GtkMapPriv *priv;
1278
1279 g_return_val_if_fail(GTK_IS_MAP(widget), -1);
1280
1281 map=GTK_MAP(widget);
1282 priv=GTK_MAP_GET_PRIVATE(map);
1283
1284 nzoom=priv->zoom+zdir;
1285 if ((nzoom >= 0) && (nzoom < priv->max_zoom - 1)) {
1286         gtk_map_set_zoom(widget, nzoom);
1287 }
1288 return nzoom;
1289 }
1290
1291 gboolean
1292 gtk_map_zoom_in(GtkWidget *widget)
1293 {
1294 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1295 gtk_map_zoom(widget, -1);
1296 return FALSE;
1297 }
1298
1299 gboolean
1300 gtk_map_zoom_out(GtkWidget *widget)
1301 {
1302 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1303 gtk_map_zoom(widget, 1);
1304 return FALSE;
1305 }
1306
1307 void
1308 gtk_map_set_cache_size(GtkWidget *widget, guint cache_size)
1309 {
1310 GtkMap *map;
1311 GtkMapPriv *priv;
1312
1313 g_return_if_fail(GTK_IS_MAP(widget));
1314 map=GTK_MAP(widget);
1315 priv=GTK_MAP_GET_PRIVATE(map);
1316 if (cache_size>512)
1317         cache_size=512;
1318 image_cache_set_size(priv->icache, cache_size);
1319 }
1320
1321 /**
1322  * Mouse scroller zoom in/out callback
1323  */
1324 static gboolean
1325 gtk_map_scroll_event_cb(GtkWidget *widget, GdkEventScroll *event)
1326 {
1327 GtkMap *map;
1328 GtkMapPriv *priv;
1329
1330 g_return_if_fail(GTK_IS_MAP(widget));
1331
1332 map=GTK_MAP(widget);
1333 priv=GTK_MAP_GET_PRIVATE(map);
1334 gtk_map_zoom(widget, event->direction==GDK_SCROLL_UP ? -1 : 1);
1335 if (priv->zoom_to_mouse)
1336         gtk_map_set_center(widget, x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1337 return FALSE;
1338 }