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