]> 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         GtkMenu *menu;
74
75         PangoContext *context;
76         PangoLayout *layout;
77         PangoFontDescription *fontdesc;
78
79         PangoContext *speed_context;
80         PangoLayout *speed_layout;
81         PangoFontDescription *speed_font;
82
83         PangoContext *scale_context;
84         PangoLayout *scale_layout;
85         PangoFontDescription *scale_font;
86
87         GdkGC *gc_track;
88         GdkGC *gc_route;
89         GdkGC *gc_waypoint;
90         GdkGC *gc_break;
91
92         GdkGC *gc_mark;
93         GdkGC *gc_velvec;
94
95         GdkGC *speed_gc1;
96         GdkGC *speed_gc2;
97         GdkGC *speed_gc;
98
99         /* OpenGL data */
100 #ifdef WITH_GL
101         GdkGLConfig* gl_config;
102 #endif
103         gboolean gl;
104
105         GdkRectangle scale_rect;
106
107         RepoData *curr_repo;
108
109         GTimer *timer;
110         ImageCache *icache;
111
112         GSList *markers;
113
114         /* Cached Location dot x,y values */
115         gint mark_x1;
116         gint mark_x2;
117         gint mark_y1;
118         gint mark_y2;
119         GdkRectangle mark_rect;
120
121         GtkMapCenterMode center_mode;
122         Point center;
123         Point min_center;
124         Point max_center;
125         Point focus;
126
127         /* Our "location" on the map */
128         Point location;
129
130         guint lead_ratio;
131         guint center_ratio;
132
133         gint vel_offsetx;
134         gint vel_offsety;
135
136         guint base_tilex;
137         guint base_tiley;
138
139         /* Zoom settings */
140         gint zoom;
141         gint max_zoom;
142         gint min_zoom;
143         gboolean zoom_to_mouse;
144
145         gfloat units_conv;
146         gchar *units_str;
147
148         gfloat speed;
149
150         /* Buffer offset */
151         gint offsetx;
152         gint offsety;
153
154         guint screen_grids_halfwidth;
155         guint screen_grids_halfheight;
156         guint screen_width_pixels;
157         guint screen_height_pixels;
158
159         guint focus_unitwidth;
160         guint focus_unitheight;
161         guint world_size_tiles;
162
163         gint show_paths;
164         gboolean show_scale;
165         gboolean show_location;
166         gboolean show_velvec;
167         gboolean show_markers;
168         gboolean show_speed;
169         gboolean click_to_center;
170         gboolean zoom_in_on_2button;
171         gboolean rotate_view;
172         gfloat rotate_angle;
173
174         guint draw_width;
175
176         gboolean fast_render;
177
178         guint key_zoom_new;
179         guint key_zoom_timeout_sid;
180
181         /* Paths */
182         GSList *paths;                  /* A list with paths to draw (tracks, routes, friends) */
183         Path *current_track;    /* Pointer to main track */
184         Path *current_route;    /* Pointer to main route */
185 };
186
187 #define tile2grid(tile) ((tile) << 3)
188 #define grid2tile(grid) ((grid) >> 3)
189 #define tile2pixel(tile) ((tile) << 8)
190 #define pixel2tile(pixel) ((pixel) >> 8)
191 #define tile2unit(tile) ((tile) << (8 + priv->zoom))
192 #define unit2tile(unit) ((unit) >> (8 + priv->zoom))
193 #define tile2zunit(tile, zoom) ((tile) << (8 + zoom))
194 #define unit2ztile(unit, zoom) ((unit) >> (8 + zoom))
195
196 #define grid2pixel(grid) ((grid) << 5)
197 #define pixel2grid(pixel) ((pixel) >> 5)
198 #define grid2unit(grid) ((grid) << (5 + priv->zoom))
199 #define unit2grid(unit) ((unit) >> (5 + priv->zoom))
200
201 #define pixel2unit(pixel) ((pixel) << priv->zoom)
202 #define unit2pixel(pixel) ((pixel) >> priv->zoom)
203 #define pixel2zunit(pixel, zoom) ((pixel) << (zoom))
204
205 #define unit2bufx(unit) (unit2pixel(unit) - tile2pixel(priv->base_tilex))
206 #define bufx2unit(x) (pixel2unit(x) + tile2unit(priv->base_tilex))
207 #define unit2bufy(unit) (unit2pixel(unit) - tile2pixel(priv->base_tiley))
208 #define bufy2unit(y) (pixel2unit(y) + tile2unit(priv->base_tiley))
209
210 #define unit2x(unit) (unit2pixel(unit) - tile2pixel(priv->base_tilex) - priv->offsetx)
211 #define x2unit(x) (pixel2unit(x + priv->offsetx) + tile2unit(priv->base_tilex))
212 #define unit2y(unit) (unit2pixel(unit) - tile2pixel(priv->base_tiley) - priv->offsety)
213 #define y2unit(y) (pixel2unit(y + priv->offsety) + tile2unit(priv->base_tiley))
214
215 #define leadx2unit (priv->location.unitx + (priv->lead_ratio) * pixel2unit(priv->vel_offsetx))
216 #define leady2unit (priv->location.unity + (0.6f*priv->lead_ratio) * pixel2unit(priv->vel_offsety))
217
218 #define GTK_MAP_TILE_SIZE_PIXELS (256)
219 #define GTK_MAP_TILE_SIZE_P2 (8)
220
221 /* #define GTK_MAP_WORLD_SIZE_UNITS(max_zoom) (2 << (max_zoom + GTK_MAP_TILE_SIZE_P2)) */
222
223 #define GTK_MAP_WORLD_SIZE_UNITS (2<<26)
224 #define WORLD_SIZE_UNITS GTK_MAP_WORLD_SIZE_UNITS
225
226 /* Pans are done two "grids" at a time, or 64 pixels. */
227 #define GTK_MAP_PAN_UNITS (grid2unit(2))
228
229 #define BOUND(x, a, b) { \
230         if((x) < (a)) \
231                 (x) = (a); \
232         else if((x) > (b)) \
233                 (x) = (b); \
234 }
235
236 static void gtk_map_finalize(GObject *object);
237
238 static void gtk_map_size_request(GtkWidget *widget, GtkRequisition *requisition);
239 static void gtk_map_size_allocate(GtkWidget *widget, GtkAllocation *allocate);
240 static void gtk_map_realize(GtkWidget *widget);
241 static gboolean gtk_map_expose(GtkWidget *widget, GdkEventExpose *event);
242
243 static void gtk_map_scale_draw(GtkWidget *widget, GdkEventExpose *event);
244 static void gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event);
245 static void gtk_map_speed_draw(GtkWidget *widget, GdkEventExpose *event);
246
247 static void gtk_map_render_buffer(GtkWidget *widget, GdkEventExpose *event);
248 static void gtk_map_render_markers(GtkWidget *widget, GdkEventExpose *event);
249 static void gtk_map_render_paths(GtkWidget *widget, GdkEventExpose *event);
250
251 static gboolean gtk_map_update_buffer_size(GtkWidget *widget, gint new_width, gint new_height);
252 static void gtk_map_update_size(GtkWidget *widget, gint width, gint height);
253
254 static void gtk_map_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
255 static void gtk_map_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
256
257 static gboolean gtk_map_button_press_cb(GtkWidget *widget, GdkEventButton *event);
258 static gboolean gtk_map_button_release_cb(GtkWidget *widget, GdkEventButton *event);
259 static gboolean gtk_map_scroll_event_cb(GtkWidget *widget, GdkEventScroll *event); 
260
261 /* Signal IDs */
262 enum {
263         MAP_LOCATION_CHANGED,
264         MAP_ZOOM_CHANGED,
265         MAP_PANNED,
266         MAP_CENTER_MODE_CHANGED,
267         MARKER_CLICK,
268         LAST_SIGNAL
269 };
270
271 /* Property IDs */
272 enum {
273         LAST_PROP
274 };
275
276 static guint gtk_map_signals[LAST_SIGNAL] = { 0 };
277
278 static void
279 gtk_map_class_init(GtkMapClass *class)
280 {
281 GObjectClass *object_class;
282 GtkWidgetClass *widget_class;
283         
284 object_class = (GObjectClass*) class;
285 widget_class = (GtkWidgetClass*) class;
286         
287 object_class->finalize = gtk_map_finalize;
288 object_class->set_property = gtk_map_set_property;
289 object_class->get_property = gtk_map_get_property;
290         
291 widget_class->size_request = gtk_map_size_request;
292 widget_class->expose_event = gtk_map_expose;
293 widget_class->realize = gtk_map_realize;
294 widget_class->size_allocate = gtk_map_size_allocate;
295
296 g_type_class_add_private (object_class, sizeof(GtkMapPriv));
297
298 gtk_map_signals[MAP_ZOOM_CHANGED]=g_signal_new("zoom-changed", G_OBJECT_CLASS_TYPE(object_class),
299         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, zoom_changed),
300         NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
301
302 gtk_map_signals[MAP_LOCATION_CHANGED]=g_signal_new("location-changed", G_OBJECT_CLASS_TYPE(object_class),
303         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, location_changed),
304         NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
305
306 gtk_map_signals[MAP_LOCATION_CHANGED]=g_signal_new("center-mode-changed", G_OBJECT_CLASS_TYPE(object_class),
307         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GtkMapClass, center_mode_changed),
308         NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
309
310 }
311
312 static inline void 
313 gtk_map_recalc_center(GtkMapPriv *priv)
314 {
315 g_return_if_fail(priv);
316 switch(priv->center_mode) {
317         case CENTER_LEAD:
318                 priv->center.unitx = leadx2unit;
319                 priv->center.unity = leady2unit;
320         break;
321         case CENTER_LATLON:
322                 priv->center.unitx = priv->location.unitx;
323                 priv->center.unity = priv->location.unity;
324         break;
325         case CENTER_MANUAL:
326         default:
327                 /* Do nothing, use priv->center.* */
328         break;
329 }
330 }
331
332 static inline void
333 gtk_map_recalc_offset(GtkMapPriv *priv)
334 {
335 g_return_if_fail(priv);
336 priv->offsetx = grid2pixel(unit2grid(priv->center.unitx) - priv->screen_grids_halfwidth - tile2grid(priv->base_tilex));
337 priv->offsety = grid2pixel(unit2grid(priv->center.unity) - priv->screen_grids_halfheight - tile2grid(priv->base_tiley));
338 }
339
340 static inline void 
341 gtk_map_recalc_focus_base(GtkMapPriv *priv)
342 {
343 g_return_if_fail(priv);
344 priv->focus.unitx = x2unit(priv->screen_width_pixels * priv->center_ratio / 20);
345 priv->focus.unity = y2unit(priv->screen_height_pixels * priv->center_ratio / 20);
346 }
347
348 static inline void
349 gtk_map_recalc_focus_size(GtkMapPriv *priv)
350 {
351 g_return_if_fail(priv);
352 priv->focus_unitwidth = pixel2unit((10 - priv->center_ratio) * priv->screen_width_pixels / 10);
353 priv->focus_unitheight = pixel2unit((10 - priv->center_ratio) * priv->screen_height_pixels / 10);
354 }
355
356 static inline void 
357 gtk_map_recalc_center_bounds(GtkMapPriv *priv)
358 {
359 g_return_if_fail(priv);
360 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
361 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
362 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfwidth) - 1;
363 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS-grid2unit(priv->screen_grids_halfheight) - 1;
364 }
365
366 static void
367 gtk_map_init(GtkMap *map)
368 {
369 GtkMapPriv *priv;
370
371 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
372 priv=GTK_MAP_GET_PRIVATE(map);
373
374 priv->base_tilex=-5;
375 priv->base_tiley=-5;
376
377 priv->draw_width=4;
378
379 priv->zoom=3;
380 priv->min_zoom=0;
381 priv->max_zoom=17;
382 priv->zoom_to_mouse=TRUE;
383
384 priv->center_mode=CENTER_LATLON;
385 priv->center.unitx=0;
386 priv->center.unity=0;
387
388 priv->speed=-1;
389 priv->speed_gc=priv->speed_gc1;
390
391 priv->units_conv=1.85200;
392
393 priv->icache=image_cache_new(64);
394
395 priv->show_scale=TRUE;
396 priv->show_velvec=TRUE;
397 priv->show_markers=TRUE;
398 priv->show_location=TRUE;
399
400 priv->click_to_center=FALSE;
401 priv->zoom_in_on_2button=FALSE;
402
403 priv->rotate_angle=M_PI;
404 priv->rotate_view=FALSE;
405
406 priv->gl=FALSE;
407 priv->buffer=NULL;
408 }
409
410 static void
411 gtk_map_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
412 {
413 GtkMap *map;
414 g_return_if_fail(GTK_IS_MAP(object));
415 map=GTK_MAP(object);
416 switch (prop_id) {
417         default:
418                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
419         break;
420 }
421 }
422
423 static void
424 gtk_map_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
425 {
426 GtkMap *map;
427
428 g_return_if_fail(GTK_IS_MAP(object));
429 map=GTK_MAP(object);
430 switch (prop_id) {
431         default:
432                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
433         break;
434 }
435 }
436
437 GtkWidget*
438 gtk_map_new(void)
439 {
440 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
441 return g_object_new(GTK_MAP_TYPE, NULL);
442 }
443
444 static void
445 gtk_map_finalize(GObject *object)
446 {
447 GtkMap *map;
448 GtkMapPriv *priv;
449         
450 g_return_if_fail(GTK_IS_MAP(object));
451 map=GTK_MAP(object);
452 priv=GTK_MAP_GET_PRIVATE(map);
453
454 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
455
456 #ifdef WITH_CAIRO
457 if (priv->ct)
458         cairo_destroy(priv->ct);
459 #endif
460
461 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
462         gtk_widget_unmap(GTK_WIDGET(object));
463 }
464
465 G_OBJECT_CLASS(gtk_map_parent_class)->finalize(object);
466 }
467
468 static void
469 gtk_map_update_size(GtkWidget *widget, gint width, gint height)
470 {
471 GtkMap *map;
472 GtkMapPriv *priv;
473 guint tw, th;
474
475 g_return_if_fail(GTK_IS_MAP(widget));
476 map=GTK_MAP(widget);
477 priv=GTK_MAP_GET_PRIVATE(map);
478
479 tw=GTK_MAP_TILE_SIZE_PIXELS*((width/GTK_MAP_TILE_SIZE_PIXELS)+2);
480 th=GTK_MAP_TILE_SIZE_PIXELS*((height/GTK_MAP_TILE_SIZE_PIXELS)+2);
481
482 gtk_map_update_buffer_size(widget, tw, th);
483 if (!priv->buffer) {
484         return;
485 }
486
487 priv->screen_width_pixels = width;
488 priv->screen_height_pixels = height;
489 priv->screen_grids_halfwidth = pixel2grid(priv->screen_width_pixels) / 2;
490 priv->screen_grids_halfheight = pixel2grid(priv->screen_height_pixels) / 2;
491
492 /* Set scale_rect. */
493 priv->scale_rect.x = (priv->screen_width_pixels - SCALE_WIDTH) / 2;
494 priv->scale_rect.width = SCALE_WIDTH;
495
496 gtk_map_recalc_focus_base(priv);
497 gtk_map_recalc_focus_size(priv);
498
499 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
500 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
501 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfwidth) - 1;
502 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfheight) - 1;
503 }
504
505 static void
506 gtk_map_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
507 {
508 GtkMap *map;
509 GtkMapPriv *priv;
510
511 g_return_if_fail(GTK_IS_MAP(widget));
512 map=GTK_MAP(widget);
513 priv=GTK_MAP_GET_PRIVATE(map);
514 widget->allocation = *allocation;
515
516 g_debug("GTKMAP: Size allocate (%d, %d)", widget->allocation.width, widget->allocation.height);
517
518 if (GTK_WIDGET_REALIZED(widget)) {
519         gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
520         gtk_map_update_size(widget, allocation->width, allocation->height);
521         gtk_map_refresh(widget);
522 }
523
524 }
525
526 static void
527 gtk_map_size_request(GtkWidget  *widget, GtkRequisition *requisition)
528 {
529 GtkMap *map;
530         
531 g_return_if_fail(GTK_IS_MAP(widget));
532 g_return_if_fail(requisition != NULL);
533         
534 map=GTK_MAP(widget);
535
536 requisition->width=GTK_MAP_TILE_SIZE_PIXELS/2;
537 requisition->height=GTK_MAP_TILE_SIZE_PIXELS/2;
538 }
539
540 static gboolean
541 gtk_map_update_buffer_size(GtkWidget *widget, gint new_width, gint new_height)
542 {
543 GtkMap *map;
544 GtkMapPriv *priv;
545
546 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
547 g_return_val_if_fail(widget->window, FALSE);
548
549 map=GTK_MAP(widget);
550 priv=GTK_MAP_GET_PRIVATE(map);
551
552 g_debug("GTKMAP: %s (%d, %d)", __PRETTY_FUNCTION__, new_width, new_height);
553
554 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) )) {
555         priv->buf_width_pixels=new_width<GTK_MAP_TILE_SIZE_PIXELS ? GTK_MAP_TILE_SIZE_PIXELS : new_width;
556         priv->buf_height_pixels=new_height<GTK_MAP_TILE_SIZE_PIXELS ? GTK_MAP_TILE_SIZE_PIXELS : new_height;
557         priv->buf_width_tiles=priv->buf_width_pixels/GTK_MAP_TILE_SIZE_PIXELS;
558         priv->buf_height_tiles=priv->buf_height_pixels/GTK_MAP_TILE_SIZE_PIXELS;
559         if (priv->buffer)
560                 g_object_unref(priv->buffer);
561         priv->buffer=gdk_pixmap_new(widget->window, priv->buf_width_pixels, priv->buf_height_pixels, -1);
562         if (priv->buffer)
563                 gdk_draw_rectangle(priv->buffer, widget->style->black_gc, TRUE, 0, 0, priv->buf_width_pixels, priv->buf_height_pixels);
564         return TRUE;
565 }
566 return FALSE;
567 }
568
569 static void 
570 gtk_map_realize(GtkWidget *widget)
571 {
572 GtkMap *map;
573 GtkMapPriv *priv;
574 GdkColor color;
575 GdkWindowAttr attributes;
576 guint attributes_mask;
577
578 g_return_if_fail(GTK_IS_MAP(widget));
579 map=GTK_MAP(widget);
580 priv=GTK_MAP_GET_PRIVATE(map);
581
582 g_debug("GTKMAP: Realize");
583
584 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
585 attributes.window_type=GDK_WINDOW_CHILD;
586 attributes.x=widget->allocation.x;
587 attributes.y=widget->allocation.y;
588 attributes.width=widget->allocation.width;
589 attributes.height=widget->allocation.height;
590
591 attributes.wclass=GDK_INPUT_OUTPUT;
592 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;
593 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
594
595 attributes.visual=gtk_widget_get_visual(widget);
596 attributes.colormap=gtk_widget_get_colormap(widget);
597
598 widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
599
600 gdk_window_set_user_data(widget->window, widget);
601
602 widget->style=gtk_style_attach(widget->style, widget->window);
603 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
604
605 gtk_widget_set_extension_events(widget, GDK_EXTENSION_EVENTS_ALL);
606
607 #ifdef WITH_GL
608 priv->gl_config=gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH);
609 if (priv->gl_config) {
610     g_print("OpenGL version: %s\n", glGetString (GL_VERSION));
611     g_print("OpenGL vendor: %s\n", glGetString (GL_VENDOR));
612     g_print("OpenGL renderer: %s\n", glGetString (GL_RENDERER));
613         gtk_widget_set_gl_capability(widget, priv->gl_config, NULL, TRUE, GDK_GL_RGBA_TYPE);
614         priv->gl=TRUE;
615 }
616 #endif
617
618 priv->scale_context=gtk_widget_get_pango_context(widget);
619 priv->scale_layout=pango_layout_new(priv->scale_context);
620 priv->scale_font=pango_font_description_new();
621 pango_font_description_set_size(priv->scale_font, 12 * PANGO_SCALE);
622 pango_layout_set_font_description(priv->scale_layout, priv->scale_font);
623
624 /* Speed limit, over limit color */
625 priv->speed_gc1=gdk_gc_new(widget->window);
626 color.red=0xffff;
627 color.green=0;
628 color.blue=0;
629 gdk_gc_set_rgb_fg_color(priv->speed_gc1, &color);
630
631 /* Speed limit, under limit color */
632 priv->speed_gc2=gdk_gc_new(widget->window);
633 color.red=0;
634 color.green=0x1000;
635 color.blue=0;
636 gdk_gc_set_rgb_fg_color(priv->speed_gc2, &color);
637
638 priv->gc_track=gdk_gc_new(widget->window);
639 color.red=0xffff;
640 color.green=0;
641 color.blue=0;
642 gdk_gc_set_rgb_fg_color(priv->gc_track, &color);
643
644 priv->gc_route=gdk_gc_new(widget->window);
645 color.red=0;
646 color.green=0xffff;
647 color.blue=0;
648 gdk_gc_set_rgb_fg_color(priv->gc_route, &color);
649
650 priv->gc_waypoint=gdk_gc_new(widget->window);
651 color.red=0xffff;
652 color.green=0xffff;
653 color.blue=0;
654 gdk_gc_set_rgb_fg_color(priv->gc_waypoint, &color);
655
656 priv->gc_break=gdk_gc_new(widget->window);
657 color.red=0xffff;
658 color.green=0;
659 color.blue=0xffff;
660 gdk_gc_set_rgb_fg_color(priv->gc_break, &color);
661
662 priv->speed_context=gtk_widget_get_pango_context(widget);
663 priv->speed_layout=pango_layout_new(priv->speed_context);
664 priv->speed_font=pango_font_description_new();
665 pango_font_description_set_size(priv->speed_font, 48 * PANGO_SCALE);
666 pango_layout_set_font_description(priv->speed_layout, priv->speed_font);
667 pango_layout_set_alignment(priv->speed_layout, PANGO_ALIGN_LEFT);
668
669 g_signal_connect(G_OBJECT(map), "button_press_event", G_CALLBACK(gtk_map_button_press_cb), NULL);
670 g_signal_connect(G_OBJECT(map), "button_release_event",G_CALLBACK(gtk_map_button_release_cb), NULL);
671 g_signal_connect(G_OBJECT(map), "scroll_event",  G_CALLBACK(gtk_map_scroll_event_cb), NULL);
672
673 gtk_widget_queue_resize(widget);
674 }
675
676 static void 
677 gtk_map_render_buffer(GtkWidget *widget, GdkEventExpose *event)
678 {
679 GtkMap *map;
680 GtkMapPriv *priv;
681
682 g_return_if_fail(GTK_IS_MAP(widget));
683 g_return_if_fail(event != NULL);
684
685 map=GTK_MAP(widget);
686 priv=GTK_MAP_GET_PRIVATE(map);
687
688 if (!priv->buffer)
689         return
690
691 g_debug("GTKMAP: expose (%d, %d)-(%d, %d)", event->area.x, event->area.y, event->area.width, event->area.height);
692 g_debug("GTKMAP: expose (%d, %d)", event->area.x + priv->offsetx, event->area.y + priv->offsety);
693 g_debug("GTKMAP: expose (%d, %d)", unit2x(priv->center.unitx), unit2y(priv->center.unity));
694
695 #ifdef WITH_CAIROa
696 cairo_save(priv->ct);
697 cairo_rectangle(priv->ct, event->area.x, event->area.y, event->area.width, event->area.height);
698 cairo_clip(priv->ct);
699 #if 0
700 if (priv->rotate_view) {
701         cairo_translate(priv->ct, (gdouble)-unit2x(priv->center.unitx), (gdouble)-unit2y(priv->center.unity) );
702         cairo_rotate(priv->ct, (gdouble)priv->rotate_angle);
703         cairo_translate(priv->ct, (gdouble)unit2x(priv->center.unitx), (gdouble)unit2y(priv->center.unity) );
704 }
705 #endif
706 cairo_translate(priv->ct, -(event->area.x+priv->offsetx), -(event->area.y+priv->offsety));
707 gdk_cairo_set_source_pixmap(priv->ct, priv->buffer, event->area.x, event->area.y);
708 cairo_set_operator (priv->ct, CAIRO_OPERATOR_SOURCE);
709 cairo_paint(priv->ct);
710 cairo_restore(priv->ct);
711 #else
712 gdk_draw_drawable(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL], priv->buffer,
713         event->area.x + priv->offsetx, event->area.y + priv->offsety,
714         event->area.x, event->area.y, event->area.width, event->area.height);
715 #endif
716 }
717
718 static gboolean
719 gtk_map_expose(GtkWidget *widget, GdkEventExpose *event)
720 {
721 GtkMap *map;
722 GtkMapPriv *priv;
723
724 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
725 g_return_val_if_fail(event != NULL, FALSE);
726
727 map=GTK_MAP(widget);
728 priv=GTK_MAP_GET_PRIVATE(map);
729
730 #ifdef WITH_CAIRO
731 priv->ct=gdk_cairo_create(widget->window);
732 #endif
733
734 gtk_map_render_buffer(widget, event);
735
736 if (priv->show_paths!=0)
737         gtk_map_render_paths(widget, event);
738
739 if (priv->show_markers)
740         gtk_map_render_markers(widget, event);
741
742 if (priv->show_speed)
743         gtk_map_speed_draw(widget, event);
744
745 if (priv->show_location)
746         gtk_map_mark_draw(widget, event);
747
748 if (priv->show_scale)
749         gtk_map_scale_draw(widget, event);
750
751 #ifdef WITH_CAIRO
752 cairo_destroy(priv->ct);
753 priv->ct=NULL;
754 #endif
755
756 return TRUE;
757 }
758
759 static void 
760 gtk_map_render_markers(GtkWidget *widget, GdkEventExpose *event)
761 {
762
763 }
764
765 static void
766 gtk_map_render_waypoint(GtkWidget *widget, guint x1, guint y1)
767 {
768 GtkMap *map;
769 GtkMapPriv *priv;
770 GdkGC *gc;
771
772 g_return_if_fail(GTK_IS_MAP(widget));
773
774 map=GTK_MAP(widget);
775 priv=GTK_MAP_GET_PRIVATE(map);
776
777 gc=priv->gc_waypoint;
778
779 if ((x1 > priv->buf_width_pixels) || (y1 > priv->buf_height_pixels))
780         return;
781 gdk_draw_arc(widget->window, gc, FALSE, x1 - priv->draw_width, y1 - priv->draw_width, 2 * priv->draw_width, 2 * priv->draw_width, 0, 360 * 64);
782 }
783
784 static void
785 gtk_map_render_path_segment(GtkWidget *widget, guint unitx1, guint unity1, guint unitx2, guint unity2)
786 {
787 GtkMap *map;
788 GtkMapPriv *priv;
789 GdkGC *gc;
790 gint x1, y1, x2, y2;
791
792 g_return_if_fail(GTK_IS_MAP(widget));
793
794 map=GTK_MAP(widget);
795 priv=GTK_MAP_GET_PRIVATE(map);
796
797 x1=unit2bufx(unitx1);
798 y1=unit2bufy(unity1);
799 x2=unit2bufx(unitx2);
800 y2=unit2bufy(unity2);
801
802 /* Make sure this line could possibly be visible. */
803 if (!((x1 > priv->buf_width_pixels && x2 > priv->buf_width_pixels) || (x1 < 0 && x2 < 0)
804       || (y1 > priv->buf_height_pixels && y2 > priv->buf_height_pixels) || (y1 < 0 && y2 < 0))) {
805         gc=priv->gc_track;
806         gdk_draw_line(widget->window, gc, x1, y1, x2, y2);
807 }
808 }
809
810 static void
811 gtk_map_render_path(GtkWidget *widget, Path *path, GdkEventExpose *event)
812 {
813 GtkMap *map;
814 GtkMapPriv *priv;
815 Point *curr;
816 WayPoint *wcurr;
817
818 g_return_if_fail(path);
819
820 if (path->head==path->tail)
821         return;
822
823 map=GTK_MAP(widget);
824 priv=GTK_MAP_GET_PRIVATE(map);
825
826 for (curr = path->head, wcurr = path->whead; curr++ != path->tail;) {
827         /* Draw the line from (curr - 1) to (curr). */
828
829         gtk_map_render_path_segment(widget, curr[-1].unitx, curr[-1].unity, curr->unitx, curr->unity);
830         /* Now, check if curr is a waypoint. */
831         if (wcurr && wcurr <= path->wtail && wcurr->point == curr) {
832                 guint x1 = unit2bufx(wcurr->point->unitx);
833                 guint y1 = unit2bufy(wcurr->point->unity);
834
835                 gtk_map_render_waypoint(widget, x1, y1);
836                 wcurr++;
837         }
838 }
839 #if 0
840 if (path->type==PATH_TYPE_ROUTE)
841         gtk_map_render_next_waypoint(widget, path);
842 #endif
843 }
844
845 static void 
846 gtk_map_render_paths(GtkWidget *widget, GdkEventExpose *event)
847 {
848 GtkMap *map;
849 GtkMapPriv *priv;
850 GSList *iter;
851
852 map=GTK_MAP(widget);
853 priv=GTK_MAP_GET_PRIVATE(map);
854
855 /* No paths defined so return */
856 if (!priv->paths)
857         return;
858
859 for (iter=priv->paths; iter!=NULL; iter=iter->next) {
860         Path *p=(Path *)iter->data;
861
862         if ( (!(priv->show_paths & ROUTES_MASK) && p->type==PATH_TYPE_ROUTE) || 
863                         (!(priv->show_paths & TRACKS_MASK) && p->type==PATH_TYPE_ROUTE) )
864                 continue;
865
866         gtk_map_render_path(widget, p, event);
867 }
868 }
869
870 gboolean
871 gtk_map_add_path(GtkWidget *widget, Path *path)
872 {
873 GtkMap *map;
874 GtkMapPriv *priv;
875
876 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
877 g_return_val_if_fail(path, FALSE);
878
879 map=GTK_MAP(widget);
880 priv=GTK_MAP_GET_PRIVATE(map);
881
882 /* Don't allow duplicates */
883 if (g_slist_find(priv->paths, path)!=NULL)
884         return FALSE;
885 priv->paths=g_slist_append(priv->paths, path);
886 return TRUE;
887 }
888
889 gboolean
890 gtk_map_remove_path(GtkWidget *widget, Path *path)
891 {
892 GtkMap *map;
893 GtkMapPriv *priv;
894
895 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
896 g_return_val_if_fail(path, FALSE);
897
898 map=GTK_MAP(widget);
899 priv=GTK_MAP_GET_PRIVATE(map);
900 priv->paths=g_slist_remove(priv->paths, path);
901 return TRUE;
902 }
903
904 static void 
905 gtk_map_mark_draw(GtkWidget *widget, GdkEventExpose *event)
906 {
907 GtkMap *map;
908 GtkMapPriv *priv;
909
910 map=GTK_MAP(widget);
911 priv=GTK_MAP_GET_PRIVATE(map);
912
913 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
914
915 if (!gdk_rectangle_intersect(&event->area, &priv->mark_rect, NULL))
916         return;
917
918 return;
919
920 #ifdef WITH_CAIRO
921 cairo_save(priv->ct);
922 cairo_arc(priv->ct, priv->mark_x1, priv->mark_y1, priv->draw_width*2, 0, 2 * M_PI);
923 cairo_set_source_rgb(priv->ct, 1, 1, 1);
924 cairo_fill_preserve(priv->ct);
925 cairo_set_source_rgba(priv->ct, 0.2, 0.2, 0.8, 0.7);
926 if (priv->show_velvec) {
927         cairo_save(priv->ct);
928         cairo_set_line_width(priv->ct, priv->draw_width);
929         cairo_move_to(priv->ct, priv->mark_x1, priv->mark_y1);
930         cairo_line_to(priv->ct, priv->mark_x2, priv->mark_y2);
931         cairo_restore(priv->ct);
932 }
933 cairo_stroke(priv->ct);
934 cairo_restore(priv->ct);
935 #else
936 gdk_draw_arc(widget->window, priv->gc_mark, FALSE,
937         priv->mark_x1 - priv->draw_width,
938         priv->mark_y1 - priv->draw_width,
939         2 * priv->draw_width, 2 * priv->draw_width,
940         0, 360 * 64);
941
942 if (priv->show_velvec)
943         gdk_draw_line(widget->window, priv->gc_velvec,
944                 priv->mark_x1, priv->mark_y1, 
945                 priv->mark_x2, priv->mark_y2);
946 #endif
947 }
948
949 static void 
950 gtk_map_calculate_mark(GtkWidget *widget)
951 {
952 GtkMap *map;
953 GtkMapPriv *priv;
954
955 map=GTK_MAP(widget);
956 priv=GTK_MAP_GET_PRIVATE(map);
957
958 priv->mark_x1 = unit2x(priv->location.unitx);
959 priv->mark_y1 = unit2y(priv->location.unity);
960 priv->mark_x2 = priv->mark_x1 + (priv->show_velvec ? priv->vel_offsetx : 0);
961 priv->mark_y2 = priv->mark_y1 + (priv->show_velvec ? priv->vel_offsety : 0);
962
963 priv->mark_rect.x = MIN(priv->mark_x1, priv->mark_x2) - (2 * priv->draw_width);
964 priv->mark_rect.y = MIN(priv->mark_y1, priv->mark_y2) - (2 * priv->draw_width);
965 priv->mark_rect.width = abs(priv->mark_x1 - priv->mark_x2) + (4 * priv->draw_width);
966 priv->mark_rect.height = abs(priv->mark_y1 - priv->mark_y2) + (4 * priv->draw_width);
967 }
968
969 #define SCALE_PADDING (4)
970
971 static void 
972 gtk_map_scale_draw(GtkWidget *widget, GdkEventExpose *event)
973 {
974 GtkMap *map;
975 GtkMapPriv *priv;
976 gchar buffer[16];
977 gdouble distance;
978 gdouble lat1, lon1, lat2, lon2;
979 gint width;
980
981 g_return_if_fail(GTK_IS_MAP(widget));
982 map=GTK_MAP(widget);
983 priv=GTK_MAP_GET_PRIVATE(map);
984
985 g_debug("GTKMAP: %s", __PRETTY_FUNCTION__);
986
987 pango_layout_set_text(priv->scale_layout, "0", -1);
988 pango_layout_get_pixel_size(priv->scale_layout, NULL, &priv->scale_rect.height);
989 priv->scale_rect.y = priv->screen_height_pixels - priv->scale_rect.height - 1;
990
991 if (!gdk_rectangle_intersect(&event->area, &priv->scale_rect, NULL))
992         return;
993
994 /* Now calculate and draw the distance. */
995 unit2latlon(priv->center.unitx - pixel2unit(SCALE_WIDTH / 2 - SCALE_PADDING), priv->center.unity, lat1, lon1);
996 unit2latlon(priv->center.unitx + pixel2unit(SCALE_WIDTH / 2 - SCALE_PADDING), priv->center.unity, lat2, lon2);
997 distance=calculate_distance(lat1, lon1, lat2, lon2) * priv->units_conv;
998
999 if (distance < 1.f)
1000         g_snprintf(buffer, sizeof(buffer), "%0.2f %s", distance, priv->units_str);
1001 else if (distance < 10.f)
1002         g_snprintf(buffer, sizeof(buffer), "%0.1f %s", distance, priv->units_str);
1003 else
1004         g_snprintf(buffer, sizeof(buffer), "%0.f %s", distance, priv->units_str);
1005
1006 g_debug("SCALE: %s", buffer);
1007
1008 #ifdef WITH_CAIRO
1009 cairo_save(priv->ct);
1010 cairo_rectangle(priv->ct, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
1011 cairo_set_source_rgba(priv->ct, 1, 1, 1, 0.65);
1012 cairo_fill_preserve(priv->ct);
1013 cairo_set_source_rgba(priv->ct, 0, 0, 0, 0.81);
1014 #else
1015 gdk_draw_rectangle(widget->window, widget->style->bg_gc[GTK_WIDGET_STATE(widget)],
1016            TRUE, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
1017 gdk_draw_rectangle(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1018            FALSE, priv->scale_rect.x, priv->scale_rect.y, priv->scale_rect.width, priv->scale_rect.height);
1019 #endif
1020
1021 pango_layout_set_text(priv->scale_layout, buffer, -1);
1022 pango_layout_get_pixel_size(priv->scale_layout, &width, NULL);
1023
1024 /* Draw little hashes on the ends. */
1025 #ifdef WITH_CAIRO
1026 cairo_set_source_rgb(priv->ct, 0, 0, 0);
1027 cairo_move_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING);
1028 cairo_line_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
1029
1030 cairo_move_to(priv->ct, priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
1031 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);
1032
1033 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);
1034 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);
1035
1036 cairo_move_to(priv->ct, priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
1037 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);
1038
1039 cairo_stroke(priv->ct);
1040 cairo_restore(priv->ct);
1041 #else
1042 gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1043             priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING,
1044             priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
1045 gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1046                 priv->scale_rect.x + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2,
1047             priv->scale_rect.x + (priv->scale_rect.width - width) / 2 - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
1048 gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1049             priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 - SCALE_PADDING,
1050             priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2 + SCALE_PADDING);
1051 gdk_draw_line(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1052             priv->scale_rect.x + priv->scale_rect.width - SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2,
1053             priv->scale_rect.x + (priv->scale_rect.width + width) / 2 + SCALE_PADDING, priv->scale_rect.y + priv->scale_rect.height / 2);
1054 #endif
1055
1056 /* Draw the layout itself. */
1057 gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1058                 priv->scale_rect.x + (priv->scale_rect.width - width) / 2, priv->scale_rect.y, priv->scale_layout);
1059
1060 }
1061
1062 static void
1063 gtk_map_information_text(GtkWidget *widget, guint x, guint y, GdkGC *gc, gchar *msg)
1064 {
1065 GtkMap *map;
1066 GtkMapPriv *priv;
1067 guint width, height;
1068
1069 g_return_if_fail(GTK_IS_MAP(widget));
1070 map=GTK_MAP(widget);
1071 priv=GTK_MAP_GET_PRIVATE(map);
1072
1073 pango_layout_set_text(priv->speed_layout, msg, -1);
1074 pango_layout_get_pixel_size(priv->speed_layout, &width, &height);
1075 gtk_widget_queue_draw_area(widget, x - 5, y - 5, width * 3 + 15, height + 5);
1076 gdk_window_process_all_updates();
1077 gdk_draw_layout(widget->window, gc, x, y, priv->speed_layout);
1078 gdk_window_process_all_updates();
1079 }
1080
1081 static void
1082 gtk_map_speed_draw(GtkWidget *widget, GdkEventExpose *event)
1083 {
1084 GtkMap *map;
1085 GtkMapPriv *priv;
1086 gchar buffer[16];
1087
1088 g_return_if_fail(GTK_IS_MAP(widget));
1089 map=GTK_MAP(widget);
1090 priv=GTK_MAP_GET_PRIVATE(map);
1091
1092 if (priv->speed<0)
1093         return;
1094
1095 g_snprintf(buffer, sizeof(buffer), "%0.0f %s", priv->speed * priv->units_conv, priv->units_str);
1096 gtk_map_information_text(widget, 10, 10, priv->speed_gc, buffer);
1097 }
1098
1099 void
1100 gtk_map_set_speed(GtkWidget *widget, gfloat speed, gboolean overspeed)
1101 {
1102 GtkMap *map;
1103 GtkMapPriv *priv;
1104
1105 g_return_if_fail(GTK_IS_MAP(widget));
1106 map=GTK_MAP(widget);
1107 priv=GTK_MAP_GET_PRIVATE(map);
1108
1109 priv->speed_gc=(overspeed) ? priv->speed_gc1 : priv->speed_gc2;
1110 priv->speed=speed;
1111 }
1112
1113 /**
1114  * Do an in-place scaling of a pixbuf's pixels at the given ratio from the given source location.
1115  */
1116 static void
1117 gtk_map_pixbuf_scale_inplace(GdkPixbuf *pixbuf, guint ratio_p2, guint src_x, guint src_y)
1118 {
1119 guint dest_x = 0, dest_y = 0, dest_dim = GTK_MAP_TILE_SIZE_PIXELS;
1120 guint rowstride = gdk_pixbuf_get_rowstride(pixbuf);
1121 guint n_channels = gdk_pixbuf_get_n_channels(pixbuf);
1122 guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
1123
1124 /* Sweep through the entire dest area, copying as necessary, but
1125  * DO NOT OVERWRITE THE SOURCE AREA.  We'll copy it afterward. */
1126 do {
1127         guint src_dim = dest_dim >> ratio_p2;
1128         guint src_endx = src_x - dest_x + src_dim;
1129         gint x, y;
1130
1131         for (y = dest_dim - 1; y >= 0; y--) {
1132                 guint src_offset_y, dest_offset_y;
1133
1134                 src_offset_y = (src_y + (y >> ratio_p2)) * rowstride;
1135                 dest_offset_y = (dest_y + y) * rowstride;
1136                 x = dest_dim - 1;
1137
1138                 if ((unsigned)(dest_y + y - src_y) < src_dim && (unsigned)(dest_x + x - src_x) < src_dim)
1139                         x -= src_dim;
1140
1141                 for (; x >= 0; x--) {
1142                         guint src_offset, dest_offset, i;
1143
1144                         src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * n_channels;
1145                         dest_offset = dest_offset_y + (dest_x + x) * n_channels;
1146
1147                         pixels[dest_offset] = pixels[src_offset];
1148                         for (i = n_channels - 1; i; i--)
1149                                 pixels[dest_offset + i] = pixels[src_offset + i];
1150
1151                         if ((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx)
1152                                 x -= src_dim;
1153                 }
1154         }
1155
1156         /* Reuse src_dim and src_endx to store new src_x and src_y. */
1157         src_dim = src_x + ((src_x - dest_x) >> ratio_p2);
1158         src_endx = src_y + ((src_y - dest_y) >> ratio_p2);
1159         dest_x = src_x;
1160         dest_y = src_y;
1161         src_x = src_dim;
1162         src_y = src_endx;
1163 }
1164 while ((dest_dim >>= ratio_p2) > 1);
1165 }
1166
1167 static GdkPixbuf *
1168 gtk_map_tile_load(GtkWidget *widget, guint tilex, guint tiley, gint zoff, gboolean fast_fail)
1169 {
1170 GtkMap *map;
1171 GtkMapPriv *priv;
1172 GdkPixbuf *pixbuf;
1173 GError *error = NULL;
1174 gchar buffer[BUFFER_SIZE];
1175 gchar key[BUFFER_SIZE];
1176 struct stat tstat;
1177 gint se;
1178
1179 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1180 map=GTK_MAP(widget);
1181 priv=GTK_MAP_GET_PRIVATE(map);
1182
1183 g_snprintf(buffer, sizeof(buffer), "%s/%u/%u/%u.jpg", priv->curr_repo->cache_dir, priv->zoom + zoff, (tilex >> zoff), (tiley >> zoff));
1184 g_snprintf(key, sizeof(key), "%s/%u/%u/%u", priv->curr_repo->cache_dir, priv->zoom + zoff, (tilex >> zoff), (tiley >> zoff));
1185
1186 g_debug("LOAD: %u, %u @ (%d+%d): %s", tilex, tiley, priv->zoom, zoff, buffer);
1187
1188 pixbuf=image_cache_get(priv->icache, key, buffer);
1189 if (!pixbuf) {
1190 #if 0
1191         g_unlink(buffer);
1192
1193         if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
1194                 if (download)
1195                         map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
1196         }
1197 #endif
1198         return NULL;
1199 }
1200
1201 g_object_ref(pixbuf);
1202
1203 #if 0
1204 /* Check if we need to trim. */
1205 if (gdk_pixbuf_get_width(pixbuf) != TILE_SIZE_PIXELS || gdk_pixbuf_get_height(pixbuf) != TILE_SIZE_PIXELS)
1206         pixbuf=pixbuf_trim(pixbuf);
1207 #endif
1208
1209 #if 0
1210 /* Check tile age, if file date is ower a week old, redownload if autodownload enabled */
1211 se=stat(buffer, &tstat);
1212 if (se==0) {
1213         time_t t;
1214         t=time(NULL);
1215         if (t-tstat.st_mtime>TILE_MAX_AGE) {
1216                 if (_auto_download && _curr_repo->type != REPOTYPE_NONE && !((_zoom + zoff - (_curr_repo->double_size ? 1 : 0)) % _curr_repo->dl_zoom_steps)) {
1217                         if (download) {
1218                                 g_debug("Tile: %s is old, re-downloading\n", buffer);
1219                                 map_initiate_download(tilex >> zoff, tiley >> zoff, _zoom + zoff, -INITIAL_DOWNLOAD_RETRIES);
1220                                 image_cache_invalidate(map_ic, key);
1221                         }
1222                 }
1223         }
1224 }
1225 #endif
1226
1227 return pixbuf;
1228 }
1229
1230 void 
1231 gtk_map_set_tile_repository(GtkWidget *widget, RepoData *rd)
1232 {
1233 GtkMap *map;
1234 GtkMapPriv *priv;
1235
1236 g_return_if_fail(GTK_IS_MAP(widget));
1237 map=GTK_MAP(widget);
1238 priv=GTK_MAP_GET_PRIVATE(map);
1239
1240 priv->curr_repo=rd;
1241 }
1242
1243 static gboolean
1244 gtk_map_render_tile(GtkWidget *widget, guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail)
1245 {
1246 GtkMap *map;
1247 GtkMapPriv *priv;
1248 GdkPixbuf *pixbuf=NULL;
1249 gint zoff;
1250
1251 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1252 map=GTK_MAP(widget);
1253 priv=GTK_MAP_GET_PRIVATE(map);
1254
1255 g_debug("RT: %u %u (%u) %u %u (%u, %u)", tilex, tiley, 
1256         priv->world_size_tiles, destx, desty, priv->buf_width_tiles, priv->buf_height_tiles);
1257
1258 g_return_val_if_fail(priv->buffer, FALSE);
1259 g_return_val_if_fail(priv->curr_repo, FALSE);
1260
1261 if (destx > priv->buf_width_pixels || desty > priv->buf_height_pixels)
1262         return FALSE;
1263
1264 if (tilex > priv->world_size_tiles || tiley > priv->world_size_tiles)
1265         return FALSE;
1266
1267 for (zoff = (priv->curr_repo->double_size ? 1 : 0); !pixbuf && (priv->zoom + zoff) <= priv->max_zoom && zoff <= GTK_MAP_TILE_SIZE_P2; zoff += 1) {
1268         pixbuf=gtk_map_tile_load(map, tilex, tiley, zoff, !fast_fail);
1269         if (!pixbuf) {
1270                 if (!fast_fail)
1271                         fast_fail=TRUE;
1272         } else {
1273                 /* Check if we need to blit. */
1274                 if (zoff) {
1275                         gtk_map_pixbuf_scale_inplace(pixbuf, zoff,
1276                                 (tilex - ((tilex >> zoff) << zoff)) << (GTK_MAP_TILE_SIZE_P2 - zoff),
1277                                 (tiley - ((tiley >> zoff) << zoff)) << (GTK_MAP_TILE_SIZE_P2 - zoff));
1278                         image_cache_invalidate_by_image(priv->icache, pixbuf);
1279                 }
1280         }
1281 }
1282
1283 if (pixbuf) {
1284         gdk_draw_pixbuf(priv->buffer, 
1285             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1286                 pixbuf, 0, 0, destx, desty, 
1287                 GTK_MAP_TILE_SIZE_PIXELS, 
1288                 GTK_MAP_TILE_SIZE_PIXELS, 
1289                 GDK_RGB_DITHER_NONE, 0, 0);
1290         g_object_unref(pixbuf);
1291         return TRUE;
1292 }
1293 gdk_draw_rectangle(priv->buffer, 
1294         widget->style->black_gc, TRUE, 
1295         destx, desty, 
1296         GTK_MAP_TILE_SIZE_PIXELS, 
1297         GTK_MAP_TILE_SIZE_PIXELS);
1298 return TRUE;
1299 }
1300
1301 void
1302 gtk_map_set_center(GtkWidget *widget, guint unitx, guint unity)
1303 {
1304 GtkMap *map;
1305 GtkMapPriv *priv;
1306 GtkStyle *style;
1307 gint new_base_tilex, new_base_tiley;
1308 guint new_x, new_y;
1309 guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy;
1310
1311 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1312
1313 map=GTK_MAP(widget);
1314 priv=GTK_MAP_GET_PRIVATE(map);
1315 style=widget->style;
1316
1317 priv->center_mode=CENTER_MANUAL;
1318
1319 /* Assure that _center.unitx/y are bounded. */
1320 BOUND(unitx, priv->min_center.unitx, priv->max_center.unitx);
1321 BOUND(unity, priv->min_center.unity, priv->max_center.unity);
1322
1323 priv->center.unitx = unitx;
1324 priv->center.unity = unity;
1325
1326 new_base_tilex = grid2tile((gint) pixel2grid((gint)unit2pixel((gint) priv->center.unitx)) - (gint)priv->screen_grids_halfwidth);
1327 new_base_tiley = grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
1328
1329 /* Same zoom level, so it's likely that we can reuse some of the old buffer's pixels. */
1330
1331 if (new_base_tilex != priv->base_tilex || new_base_tiley != priv->base_tiley) {
1332         /* If copying from old parts to new parts, we need to make sure we
1333          * don't overwrite the old parts when copying, so set up new_x,
1334          * new_y, old_x, old_y, iox, and ioy with that in mind. */
1335         if (new_base_tiley < priv->base_tiley) {
1336                 /* New is lower than old - start at bottom and go up. */
1337                 new_y = priv->buf_height_tiles - 1;
1338                 ioy = -1;
1339         } else {
1340                 /* New is higher than old - start at top and go down. */
1341                 new_y = 0;
1342                 ioy = 1;
1343         }
1344         if (new_base_tilex < priv->base_tilex) {
1345                 /* New is righter than old - start at right and go left. */
1346                 base_new_x = priv->buf_width_tiles - 1;
1347                 iox = -1;
1348         } else {
1349                 /* New is lefter than old - start at left and go right. */
1350                 base_new_x = 0;
1351                 iox = 1;
1352         }
1353
1354         /* Iterate over the y tile values. */
1355         old_y = new_y + new_base_tiley - priv->base_tiley;
1356         base_old_x = base_new_x + new_base_tilex - priv->base_tilex;
1357         priv->base_tilex = new_base_tilex;
1358         priv->base_tiley = new_base_tiley;
1359
1360         for (j = 0; j < priv->buf_height_tiles; ++j, new_y += ioy, old_y += ioy) {
1361                 new_x = base_new_x;
1362                 old_x = base_old_x;
1363                 /* Iterate over the x tile values. */
1364                 for (k = 0; k < priv->buf_width_tiles; ++k, new_x += iox, old_x += iox) {
1365                         /* Can we get this grid block from the old buffer?. */
1366                         if (old_x >= 0 && old_x < priv->buf_width_tiles && old_y >= 0 && old_y < priv->buf_height_tiles) {
1367                                 /* Copy from old buffer to new buffer. */
1368                                 gdk_draw_drawable(priv->buffer,
1369                                             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1370                                                 priv->buffer,
1371                                                 old_x * GTK_MAP_TILE_SIZE_PIXELS,
1372                                                 old_y * GTK_MAP_TILE_SIZE_PIXELS,
1373                                                 new_x * GTK_MAP_TILE_SIZE_PIXELS,
1374                                                 new_y * GTK_MAP_TILE_SIZE_PIXELS,
1375                                                 GTK_MAP_TILE_SIZE_PIXELS,
1376                                                 GTK_MAP_TILE_SIZE_PIXELS);
1377                         } else {
1378                                 gtk_map_render_tile(GTK_MAP(map), 
1379                                                 new_base_tilex + new_x,
1380                                                 new_base_tiley + new_y,
1381                                                 new_x * GTK_MAP_TILE_SIZE_PIXELS,
1382                                                 new_y * GTK_MAP_TILE_SIZE_PIXELS,
1383                                                 priv->fast_render);
1384                         }
1385                 }
1386         }
1387 }
1388
1389 gtk_map_recalc_offset(priv);
1390 gtk_map_recalc_focus_base(priv);
1391 gtk_map_refresh(widget);
1392 g_signal_emit(widget, gtk_map_signals[MAP_LOCATION_CHANGED], 0, priv->zoom);
1393 }
1394
1395 void
1396 gtk_map_set_center_latlon(GtkWidget *widget, gdouble lat, gdouble lon)
1397 {
1398 guint unitx, unity;
1399
1400 latlon2unit(lat, lon, unitx, unity);
1401 gtk_map_set_center(widget, unitx, unity);
1402 }
1403
1404 void
1405 gtk_map_pan(GtkWidget *widget, gint delta_x, gint delta_y)
1406 {
1407 GtkMap *map;
1408 GtkMapPriv *priv;
1409
1410 g_return_if_fail(GTK_IS_MAP(widget));
1411 map=GTK_MAP(widget);
1412 priv=GTK_MAP_GET_PRIVATE(map);
1413
1414 gtk_map_set_center(widget, priv->center.unitx + delta_x*GTK_MAP_PAN_UNITS, priv->center.unity + delta_y*GTK_MAP_PAN_UNITS);
1415 }
1416
1417 void
1418 gtk_map_refresh(GtkWidget *widget)
1419 {
1420 GtkMap *map;
1421 GtkMapPriv *priv;
1422 guint x, y;
1423
1424 g_return_if_fail(GTK_IS_MAP(widget));
1425 map=GTK_MAP(widget);
1426 priv=GTK_MAP_GET_PRIVATE(map);
1427
1428 for (y = 0; y < priv->buf_height_tiles; ++y)
1429         for (x = 0; x < priv->buf_width_tiles; ++x)
1430                 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);
1431
1432 gtk_widget_queue_draw(widget);
1433 }
1434
1435 gint 
1436 gtk_map_get_zoom(GtkWidget *widget)
1437 {
1438 GtkMap *map;
1439 GtkMapPriv *priv;
1440
1441 g_return_val_if_fail(GTK_IS_MAP(widget), -1);
1442
1443 map=GTK_MAP(widget);
1444 priv=GTK_MAP_GET_PRIVATE(map);
1445
1446 return priv->zoom;
1447 }
1448
1449 gboolean
1450 gtk_map_set_zoom(GtkWidget *widget, gint new_zoom)
1451 {
1452 GtkMap *map;
1453 GtkMapPriv *priv;
1454
1455 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1456
1457 map=GTK_MAP(widget);
1458 priv=GTK_MAP_GET_PRIVATE(map);
1459
1460 g_debug("GTKMAP: zoom %d", new_zoom);
1461
1462 if (new_zoom > (priv->max_zoom - 1))
1463         return FALSE;
1464
1465 if (new_zoom == priv->zoom)
1466         return FALSE;
1467
1468 if (priv->curr_repo)
1469         priv->zoom = new_zoom / priv->curr_repo->view_zoom_steps * priv->curr_repo->view_zoom_steps;
1470 else
1471         priv->zoom = new_zoom;
1472
1473 priv->world_size_tiles = unit2tile(GTK_MAP_WORLD_SIZE_UNITS);
1474
1475 /* If we're leading, update the center to reflect new zoom level. */
1476 gtk_map_recalc_center(priv);
1477
1478 /* Update center bounds to reflect new zoom level. */
1479 priv->min_center.unitx = pixel2unit(grid2pixel(priv->screen_grids_halfwidth));
1480 priv->min_center.unity = pixel2unit(grid2pixel(priv->screen_grids_halfheight));
1481 priv->max_center.unitx = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfwidth) - 1;
1482 priv->max_center.unity = GTK_MAP_WORLD_SIZE_UNITS - grid2unit(priv->screen_grids_halfheight) - 1;
1483
1484 BOUND(priv->center.unitx, priv->min_center.unitx, priv->max_center.unitx);
1485 BOUND(priv->center.unity, priv->min_center.unity, priv->max_center.unity);
1486
1487 priv->base_tilex = grid2tile((gint) pixel2grid((gint) unit2pixel((gint) priv->center.unitx)) - (gint) priv->screen_grids_halfwidth);
1488 priv->base_tiley = grid2tile(pixel2grid(unit2pixel(priv->center.unity)) - priv->screen_grids_halfheight);
1489
1490 /* New zoom level, so we can't reuse the old buffer's pixels. Update state variables. */
1491 gtk_map_recalc_offset(priv);
1492 gtk_map_recalc_focus_base(priv);
1493 gtk_map_recalc_focus_size(priv);
1494
1495 gtk_map_refresh(widget);
1496 g_signal_emit(widget, gtk_map_signals[MAP_ZOOM_CHANGED], 0, priv->zoom);
1497
1498 return TRUE;
1499 }
1500
1501 gint 
1502 gtk_map_zoom(GtkWidget *widget, gint zdir)
1503 {
1504 gint nzoom;
1505 GtkMap *map;
1506 GtkMapPriv *priv;
1507
1508 g_return_val_if_fail(GTK_IS_MAP(widget), -1);
1509
1510 map=GTK_MAP(widget);
1511 priv=GTK_MAP_GET_PRIVATE(map);
1512
1513 nzoom=priv->zoom+zdir;
1514 if ((nzoom >= 0) && (nzoom < priv->max_zoom - 1)) {
1515         gtk_map_set_zoom(widget, nzoom);
1516 }
1517 return nzoom;
1518 }
1519
1520 gboolean
1521 gtk_map_zoom_in(GtkWidget *widget)
1522 {
1523 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1524 gtk_map_zoom(widget, -1);
1525 return FALSE;
1526 }
1527
1528 gboolean
1529 gtk_map_zoom_out(GtkWidget *widget)
1530 {
1531 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1532 gtk_map_zoom(widget, 1);
1533 return FALSE;
1534 }
1535
1536 void
1537 gtk_map_set_cache_size(GtkWidget *widget, guint cache_size)
1538 {
1539 GtkMap *map;
1540 GtkMapPriv *priv;
1541
1542 g_return_if_fail(GTK_IS_MAP(widget));
1543 map=GTK_MAP(widget);
1544 priv=GTK_MAP_GET_PRIVATE(map);
1545 if (cache_size>512)
1546         cache_size=512;
1547 image_cache_set_size(priv->icache, cache_size);
1548 }
1549
1550 /******************************************************************************/
1551 /* Internal widget callback handlers                                          */
1552 /******************************************************************************/
1553
1554 /**
1555  * Mouse scroller zoom in/out callback
1556  */
1557 static gboolean
1558 gtk_map_scroll_event_cb(GtkWidget *widget, GdkEventScroll *event)
1559 {
1560 GtkMap *map;
1561 GtkMapPriv *priv;
1562
1563 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1564
1565 map=GTK_MAP(widget);
1566 priv=GTK_MAP_GET_PRIVATE(map);
1567 gtk_map_zoom(widget, event->direction==GDK_SCROLL_UP ? -1 : 1);
1568 if (priv->zoom_to_mouse)
1569         gtk_map_set_center(widget, x2unit((gint) (event->x + 0.5)), y2unit((gint) (event->y + 0.5)));
1570 return FALSE;
1571 }
1572
1573 static gboolean
1574 gtk_map_button_press_cb(GtkWidget *widget, GdkEventButton *event)
1575 {
1576 GtkMap *map;
1577 GtkMapPriv *priv;
1578
1579 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1580 map=GTK_MAP(widget);
1581 priv=GTK_MAP_GET_PRIVATE(map);
1582
1583 switch (event->button) {
1584 case 1:
1585         if (priv->click_to_center) {
1586                 gtk_map_set_center(widget, x2unit((gint) (event->x+0.5)), y2unit((gint) (event->y+0.5)));
1587                 return FALSE;
1588         } else if (event->type==GDK_2BUTTON_PRESS) {
1589                 gtk_map_set_center(widget, x2unit((gint) (event->x+0.5)), y2unit((gint) (event->y+0.5)));
1590                 if (priv->zoom_in_on_2button)
1591                         gtk_map_zoom_in(widget);
1592                 return FALSE;
1593         }
1594 break;
1595 case 2:
1596
1597 break;
1598 case 3:
1599         if (priv->menu)
1600                 gtk_menu_popup(GTK_MENU(priv->menu), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time());
1601 break;
1602 }
1603 return FALSE;
1604 }
1605
1606 static gboolean
1607 gtk_map_button_release_cb(GtkWidget *widget, GdkEventButton *event)
1608 {
1609 GtkMap *map;
1610 GtkMapPriv *priv;
1611
1612 g_return_val_if_fail(GTK_IS_MAP(widget), FALSE);
1613 map=GTK_MAP(widget);
1614 priv=GTK_MAP_GET_PRIVATE(map);
1615
1616 switch (event->button) {
1617 case 1:
1618
1619 break;
1620 case 2:
1621
1622 break;
1623 case 3:
1624
1625 break;
1626 }
1627 return FALSE;
1628 }