1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4
* Copyright (C) 2000-2001 Ximian, Inc.
6
* Authors: Hans Petter Jansson <hpj@ximian.com>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
26
#include <gdk/gdkkeysyms.h>
27
#include <gtk/gtksignal.h>
28
#include <gdk-pixbuf/gdk-pixbuf.h>
29
#include <libart_lgpl/art_filterlevel.h>
32
#include "e-map-marshal.h"
34
/* Scroll step increment */
36
#define SCROLL_STEP_SIZE 32
41
#define E_MAP_GET_WIDTH(map) gdk_pixbuf_get_width(((EMapPrivate *) E_MAP(map)->priv)->map_render_pixbuf)
42
#define E_MAP_GET_HEIGHT(map) gdk_pixbuf_get_height(((EMapPrivate *) E_MAP(map)->priv)->map_render_pixbuf)
45
/* Zoom state - keeps track of animation hacks */
57
/* Private part of the EMap structure */
61
/* Pointer to map image */
62
GdkPixbuf *map_pixbuf, *map_render_pixbuf;
65
gboolean frozen, smooth_zoom;
67
/* Adjustments for scrolling */
71
/* Signal ids for adjustments */
72
gulong hadj_signal_id;
73
gulong vadj_signal_id;
75
/* Current scrolling offsets */
78
/* Realtime zoom data */
79
EMapZoomState zoom_state;
80
double zoom_target_long, zoom_target_lat;
96
static guint e_map_signals[LAST_SIGNAL];
99
/* Internal prototypes */
101
static void e_map_class_init (EMapClass *class);
102
static void e_map_init (EMap *view);
103
static void e_map_destroy (GtkObject *object);
104
static void e_map_finalize (GObject *object);
105
static void e_map_unmap (GtkWidget *widget);
106
static void e_map_realize (GtkWidget *widget);
107
static void e_map_unrealize (GtkWidget *widget);
108
static void e_map_size_request (GtkWidget *widget, GtkRequisition *requisition);
109
static void e_map_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
110
static gint e_map_button_press (GtkWidget *widget, GdkEventButton *event);
111
static gint e_map_button_release (GtkWidget *widget, GdkEventButton *event);
112
static gint e_map_motion (GtkWidget *widget, GdkEventMotion *event);
113
static gint e_map_expose (GtkWidget *widget, GdkEventExpose *event);
114
static gint e_map_key_press (GtkWidget *widget, GdkEventKey *event);
115
static void e_map_set_scroll_adjustments (GtkWidget *widget, GtkAdjustment *hadj, GtkAdjustment *vadj);
117
static void update_render_pixbuf (EMap *map, ArtFilterLevel interp, gboolean render_overlays);
118
static void set_scroll_area (EMap *view);
119
static void request_paint_area (EMap *view, GdkRectangle *area);
120
static void center_at (EMap *map, int x, int y, gboolean scroll);
121
static void smooth_center_at (EMap *map, int x, int y);
122
static void scroll_to (EMap *view, int x, int y);
123
static void zoom_do (EMap *map);
124
static gint load_map_background (EMap *view, gchar *name);
125
static void adjustment_changed_cb (GtkAdjustment *adj, gpointer data);
126
static void update_and_paint (EMap *map);
127
static void update_render_point (EMap *map, EMapPoint *point);
128
static void repaint_point (EMap *map, EMapPoint *point);
130
static GtkWidgetClass *parent_class;
133
/* ----------------- *
134
* Widget management *
135
* ----------------- */
142
* Registers the #EMap class if necessary, and returns the type ID
145
* Return value: The type ID of the #EMap class.
149
e_map_get_type (void)
151
static GtkType e_map_type = 0;
155
static const GTypeInfo e_map_info =
158
NULL, /* base init */
159
NULL, /* base finalize */
160
(GClassInitFunc) e_map_class_init,
161
NULL, /* class finalize */
162
NULL, /* class data */
165
(GInstanceInitFunc) e_map_init,
166
NULL, /* value table */
169
e_map_type = g_type_register_static (GTK_TYPE_WIDGET,
177
/* Class initialization function for the map view */
180
e_map_class_init (EMapClass *class)
182
GObjectClass *g_object_class;
183
GtkObjectClass *object_class;
184
GtkWidgetClass *widget_class;
186
g_object_class = (GObjectClass*) class;
187
object_class = (GtkObjectClass *) class;
188
widget_class = (GtkWidgetClass *) class;
190
parent_class = gtk_type_class (GTK_TYPE_WIDGET);
192
object_class->destroy = e_map_destroy;
194
g_object_class->finalize = e_map_finalize;
196
class->set_scroll_adjustments = e_map_set_scroll_adjustments;
197
widget_class->set_scroll_adjustments_signal =
198
g_signal_new ("set_scroll_adjustments",
199
G_OBJECT_CLASS_TYPE (object_class),
201
G_STRUCT_OFFSET (EMapClass, set_scroll_adjustments),
203
g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
205
GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
207
g_signal_new ("set_scroll_adjustments",
208
G_OBJECT_CLASS_TYPE (object_class),
210
G_STRUCT_OFFSET (EMapClass, set_scroll_adjustments),
212
_gtk_marshal_VOID__OBJECT_OBJECT,
214
GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
216
widget_class->unmap = e_map_unmap;
217
widget_class->realize = e_map_realize;
218
widget_class->unrealize = e_map_unrealize;
219
widget_class->size_request = e_map_size_request;
220
widget_class->size_allocate = e_map_size_allocate;
221
widget_class->button_press_event = e_map_button_press;
222
widget_class->button_release_event = e_map_button_release;
223
widget_class->motion_notify_event = e_map_motion;
224
widget_class->expose_event = e_map_expose;
225
widget_class->key_press_event = e_map_key_press;
229
/* Object initialization function for the map view */
232
e_map_init (EMap *view)
236
priv = g_new0 (EMapPrivate, 1);
239
load_map_background (view, MAP_DIR"/world_map-960.png");
240
priv->frozen = FALSE;
241
priv->smooth_zoom = TRUE;
242
priv->zoom_state = E_MAP_ZOOMED_OUT;
243
priv->points = g_ptr_array_new ();
245
GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS);
246
GTK_WIDGET_UNSET_FLAGS (view, GTK_NO_WINDOW);
250
/* Destroy handler for the map view */
253
e_map_destroy (GtkObject *object)
258
g_return_if_fail (object != NULL);
259
g_return_if_fail (IS_E_MAP (object));
261
view = E_MAP (object);
264
g_signal_handler_disconnect (G_OBJECT (priv->hadj), priv->hadj_signal_id);
265
g_signal_handler_disconnect (G_OBJECT (priv->vadj), priv->vadj_signal_id);
267
if (GTK_OBJECT_CLASS (parent_class)->destroy)
268
(*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
272
/* Finalize handler for the map view */
275
e_map_finalize (GObject *object)
280
g_return_if_fail (object != NULL);
281
g_return_if_fail (IS_E_MAP (object));
283
view = E_MAP (object);
286
gtk_object_unref (GTK_OBJECT (priv->hadj));
289
gtk_object_unref (GTK_OBJECT (priv->vadj));
292
if (priv->map_pixbuf)
294
gdk_pixbuf_unref (priv->map_pixbuf);
295
priv->map_pixbuf = NULL;
298
if (priv->map_render_pixbuf)
300
gdk_pixbuf_unref (priv->map_render_pixbuf);
301
priv->map_render_pixbuf = NULL;
307
if (G_OBJECT_CLASS (parent_class)->finalize)
308
G_OBJECT_CLASS (parent_class)->finalize (object);
312
/* Unmap handler for the map view */
315
e_map_unmap (GtkWidget *widget)
317
g_return_if_fail (widget != NULL);
318
g_return_if_fail (IS_E_MAP (widget));
320
if (GTK_WIDGET_CLASS (parent_class)->unmap)
321
(*GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
325
/* Realize handler for the map view */
328
e_map_realize (GtkWidget *widget)
333
g_return_if_fail (widget != NULL);
334
g_return_if_fail (IS_E_MAP (widget));
336
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
338
attr.window_type = GDK_WINDOW_CHILD;
339
attr.x = widget->allocation.x;
340
attr.y = widget->allocation.y;
341
attr.width = widget->allocation.width;
342
attr.height = widget->allocation.height;
343
attr.wclass = GDK_INPUT_OUTPUT;
344
attr.visual = gdk_rgb_get_visual ();
345
attr.colormap = gdk_rgb_get_colormap ();
346
attr.event_mask = gtk_widget_get_events (widget) |
347
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK |
348
GDK_POINTER_MOTION_MASK;
350
attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
352
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attr, attr_mask);
353
gdk_window_set_user_data (widget->window, widget);
355
widget->style = gtk_style_attach (widget->style, widget->window);
357
gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
358
update_render_pixbuf (E_MAP (widget), GDK_INTERP_BILINEAR, TRUE);
362
/* Unrealize handler for the map view */
365
e_map_unrealize (GtkWidget *widget)
367
g_return_if_fail (widget != NULL);
368
g_return_if_fail (IS_E_MAP (widget));
370
if (GTK_WIDGET_CLASS (parent_class)->unrealize)
371
(*GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
375
/* Size_request handler for the map view */
378
e_map_size_request (GtkWidget *widget, GtkRequisition *requisition)
383
g_return_if_fail (widget != NULL);
384
g_return_if_fail (IS_E_MAP (widget));
385
g_return_if_fail (requisition != NULL);
387
view = E_MAP (widget);
390
/* TODO: Put real sizes here. */
392
requisition->width = gdk_pixbuf_get_width (priv->map_pixbuf);
393
requisition->height = gdk_pixbuf_get_height (priv->map_pixbuf);
397
/* Size_allocate handler for the map view */
400
e_map_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
407
g_return_if_fail (widget != NULL);
408
g_return_if_fail (IS_E_MAP (widget));
409
g_return_if_fail (allocation != NULL);
411
view = E_MAP (widget);
417
/* Resize the window */
419
widget->allocation = *allocation;
421
if (GTK_WIDGET_REALIZED (widget))
423
gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
427
area.width = allocation->width;
428
area.height = allocation->height;
429
request_paint_area (E_MAP (widget), &area);
432
update_render_pixbuf (view, GDK_INTERP_BILINEAR, TRUE);
436
/* Button press handler for the map view */
439
e_map_button_press (GtkWidget *widget, GdkEventButton *event)
444
view = E_MAP (widget);
447
if (!GTK_WIDGET_HAS_FOCUS (widget)) gtk_widget_grab_focus (widget);
452
/* Button release handler for the map view */
455
e_map_button_release (GtkWidget *widget, GdkEventButton *event)
460
view = E_MAP (widget);
463
if (event->button != 1) return FALSE;
465
gdk_pointer_ungrab (event->time);
470
/* Motion handler for the map view */
473
e_map_motion (GtkWidget *widget, GdkEventMotion *event)
478
view = E_MAP (widget);
484
* if (event->is_hint)
485
* gdk_window_get_pointer(widget->window, &x, &y, &mods);
497
/* Expose handler for the map view */
500
e_map_expose (GtkWidget *widget, GdkEventExpose *event)
504
g_return_val_if_fail (widget != NULL, FALSE);
505
g_return_val_if_fail (IS_E_MAP (widget), FALSE);
506
g_return_val_if_fail (event != NULL, FALSE);
508
view = E_MAP (widget);
510
request_paint_area (view, &event->area);
515
/* Set_scroll_adjustments handler for the map view */
518
e_map_set_scroll_adjustments (GtkWidget *widget, GtkAdjustment *hadj, GtkAdjustment *vadj)
522
gboolean need_adjust;
524
g_return_if_fail (widget != NULL);
525
g_return_if_fail (IS_E_MAP (widget));
527
view = E_MAP (widget);
530
if (hadj) g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
531
else hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
533
if (vadj) g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
534
else vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
536
if (priv->hadj && priv->hadj != hadj)
538
g_signal_handler_disconnect (G_OBJECT (priv->hadj), priv->hadj_signal_id);
539
g_object_unref (G_OBJECT (priv->hadj));
542
if (priv->vadj && priv->vadj != vadj)
544
g_signal_handler_disconnect (G_OBJECT (priv->vadj), priv->vadj_signal_id);
545
g_object_unref (G_OBJECT (priv->vadj));
550
if (priv->hadj != hadj)
553
g_object_ref (G_OBJECT (priv->hadj));
554
gtk_object_sink (GTK_OBJECT (priv->hadj));
556
priv->hadj_signal_id = g_signal_connect (G_OBJECT (priv->hadj),
558
G_CALLBACK (adjustment_changed_cb),
564
if (priv->vadj != vadj)
567
g_object_ref (G_OBJECT (priv->vadj));
568
gtk_object_sink (GTK_OBJECT (priv->vadj));
570
priv->vadj_signal_id = g_signal_connect (G_OBJECT (priv->vadj),
572
G_CALLBACK (adjustment_changed_cb),
578
if (need_adjust) adjustment_changed_cb (NULL, view);
582
/* Key press handler for the map view */
585
e_map_key_press (GtkWidget *widget, GdkEventKey *event)
592
view = E_MAP (widget);
598
switch (event->keyval)
603
yofs = -SCROLL_STEP_SIZE;
609
yofs = SCROLL_STEP_SIZE;
614
xofs = -SCROLL_STEP_SIZE;
620
xofs = SCROLL_STEP_SIZE;
632
x = CLAMP (priv->xofs + xofs, 0, priv->hadj->upper - priv->hadj->page_size);
633
y = CLAMP (priv->yofs + yofs, 0, priv->vadj->upper - priv->vadj->page_size);
635
scroll_to (view, x, y);
637
g_signal_handler_block (G_OBJECT (priv->hadj), priv->hadj_signal_id);
638
g_signal_handler_block (G_OBJECT (priv->vadj), priv->vadj_signal_id);
640
priv->hadj->value = x;
641
priv->vadj->value = y;
643
g_signal_emit_by_name (GTK_OBJECT (priv->hadj), "value_changed");
644
g_signal_emit_by_name (GTK_OBJECT (priv->vadj), "value_changed");
646
g_signal_handler_unblock (G_OBJECT (priv->hadj), priv->hadj_signal_id);
647
g_signal_handler_unblock (G_OBJECT (priv->vadj), priv->vadj_signal_id);
654
/* ---------------- *
656
* ---------------- */
663
* Creates a new empty map widget.
665
* Return value: A newly-created map widget.
673
widget = GTK_WIDGET (g_type_create_instance (TYPE_E_MAP));
674
return (E_MAP (widget));
678
/* --- Coordinate translation --- */
681
/* These functions translate coordinates between longitude/latitude and
682
* the image x/y offsets, using the equidistant cylindrical projection.
684
* Longitude E <-180, 180]
685
* Latitude E <-90, 90] */
688
e_map_window_to_world (EMap *map, double win_x, double win_y, double *world_longitude, double *world_latitude)
693
g_return_if_fail (map);
696
g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (map)));
698
width = gdk_pixbuf_get_width (priv->map_render_pixbuf);
699
height = gdk_pixbuf_get_height (priv->map_render_pixbuf);
701
*world_longitude = (win_x + priv->xofs - (double) width / 2.0) /
702
((double) width / 2.0) * 180.0;
703
*world_latitude = ((double) height / 2.0 - win_y - priv->yofs) /
704
((double) height / 2.0) * 90.0;
709
e_map_world_to_window (EMap *map, double world_longitude, double world_latitude, double *win_x, double *win_y)
714
g_return_if_fail (map);
717
g_return_if_fail (priv->map_render_pixbuf);
718
g_return_if_fail (world_longitude >= -180.0 && world_longitude <= 180.0);
719
g_return_if_fail (world_latitude >= -90.0 && world_latitude <= 90.0);
721
width = gdk_pixbuf_get_width (priv->map_render_pixbuf);
722
height = gdk_pixbuf_get_height (priv->map_render_pixbuf);
724
*win_x = (width / 2.0 + (width / 2.0) * world_longitude / 180.0) - priv->xofs;
725
*win_y = (height / 2.0 - (height / 2.0) * world_latitude / 90.0) - priv->yofs;
728
printf ("Map size: (%d, %d)\nCoords: (%.1f, %.1f) -> (%.1f, %.1f)\n---\n", width, height, world_longitude, world_latitude, *win_x, *win_y);
737
e_map_get_magnification (EMap *map)
742
if (priv->zoom_state == E_MAP_ZOOMED_IN) return 2.0;
748
e_map_zoom_to_location (EMap *map, double longitude, double latitude)
753
g_return_if_fail (map);
754
g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (map)));
758
if (priv->zoom_state == E_MAP_ZOOMED_IN) e_map_zoom_out (map);
759
else if (priv->zoom_state != E_MAP_ZOOMED_OUT) return;
761
width = gdk_pixbuf_get_width (priv->map_render_pixbuf);
762
height = gdk_pixbuf_get_height (priv->map_render_pixbuf);
764
priv->zoom_state = E_MAP_ZOOMING_IN;
765
priv->zoom_target_long = longitude;
766
priv->zoom_target_lat = latitude;
773
e_map_zoom_out (EMap *map)
778
g_return_if_fail (map);
779
g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (map)));
783
if (priv->zoom_state != E_MAP_ZOOMED_IN) return;
785
width = gdk_pixbuf_get_width (priv->map_render_pixbuf);
786
height = gdk_pixbuf_get_height (priv->map_render_pixbuf);
788
priv->zoom_state = E_MAP_ZOOMING_OUT;
790
priv->zoom_state = E_MAP_ZOOMED_OUT;
795
e_map_set_smooth_zoom (EMap *map, gboolean state)
797
((EMapPrivate *) map->priv)->smooth_zoom = state;
802
e_map_get_smooth_zoom (EMap *map)
804
return (((EMapPrivate *) map->priv)->smooth_zoom);
809
e_map_freeze (EMap *map)
811
((EMapPrivate *) map->priv)->frozen = TRUE;
816
e_map_thaw (EMap *map)
818
((EMapPrivate *) map->priv)->frozen = FALSE;
819
update_and_paint (map);
823
/* --- Point manipulation --- */
827
e_map_add_point (EMap *map, gchar *name, double longitude, double latitude, guint32 color_rgba)
833
point = g_new0 (EMapPoint, 1);
835
point->name = name; /* Can be NULL */
836
point->longitude = longitude;
837
point->latitude = latitude;
838
point->rgba = color_rgba;
840
g_ptr_array_add (priv->points, (gpointer) point);
844
update_render_point (map, point);
845
repaint_point (map, point);
853
e_map_remove_point (EMap *map, EMapPoint *point)
858
g_ptr_array_remove (priv->points, point);
860
if (!((EMapPrivate *) map->priv)->frozen)
862
/* FIXME: Re-scaling the whole pixbuf is more than a little
863
* overkill when just one point is removed */
865
update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE);
866
repaint_point (map, point);
874
e_map_point_get_location (EMapPoint *point, double *longitude, double *latitude)
876
*longitude = point->longitude;
877
*latitude = point->latitude;
882
e_map_point_get_name (EMapPoint *point)
889
e_map_point_get_color_rgba (EMapPoint *point)
896
e_map_point_set_color_rgba (EMap *map, EMapPoint *point, guint32 color_rgba)
898
point->rgba = color_rgba;
900
if (!((EMapPrivate *) map->priv)->frozen)
902
/* TODO: Redraw area around point only */
904
update_render_point (map, point);
905
repaint_point (map, point);
911
e_map_point_set_data (EMapPoint *point, gpointer data)
913
point->user_data = data;
918
e_map_point_get_data (EMapPoint *point)
920
return point->user_data;
925
e_map_point_is_in_view (EMap *map, EMapPoint *point)
931
if (!priv->map_render_pixbuf) return FALSE;
933
e_map_world_to_window (map, point->longitude, point->latitude, &x, &y);
935
if (x >= 0 && x < GTK_WIDGET (map)->allocation.width &&
936
y >= 0 && y < GTK_WIDGET (map)->allocation.height)
944
e_map_get_closest_point (EMap *map, double longitude, double latitude, gboolean in_view)
947
EMapPoint *point_chosen = NULL, *point;
948
double min_dist = 0.0, dist;
954
for (i = 0; i < priv->points->len; i++)
956
point = g_ptr_array_index (priv->points, i);
957
if (in_view && !e_map_point_is_in_view (map, point)) continue;
959
dx = point->longitude - longitude;
960
dy = point->latitude - latitude;
961
dist = dx * dx + dy * dy;
963
if (!point_chosen || dist < min_dist)
966
point_chosen = point;
974
/* ------------------ *
975
* Internal functions *
976
* ------------------ */
980
repaint_visible (EMap *map)
986
area.width = GTK_WIDGET (map)->allocation.width;
987
area.height = GTK_WIDGET (map)->allocation.height;
989
request_paint_area (map, &area);
994
update_and_paint (EMap *map)
996
update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE);
997
repaint_visible (map);
1002
load_map_background (EMap *view, gchar *name)
1009
pb0 = gdk_pixbuf_new_from_file (name, NULL);
1010
/* pb0 = tool_load_image (name);*/
1011
if (!pb0) return (FALSE);
1013
if (priv->map_pixbuf) gdk_pixbuf_unref (priv->map_pixbuf);
1014
priv->map_pixbuf = pb0;
1015
update_render_pixbuf (view, GDK_INTERP_BILINEAR, TRUE);
1022
update_render_pixbuf (EMap *map, ArtFilterLevel interp, gboolean render_overlays)
1026
int width, height, orig_width, orig_height;
1030
if (!GTK_WIDGET_REALIZED (GTK_WIDGET (map))) return;
1032
/* Set up value shortcuts */
1035
width = GTK_WIDGET (map)->allocation.width;
1036
height = GTK_WIDGET (map)->allocation.height;
1037
orig_width = gdk_pixbuf_get_width (priv->map_pixbuf);
1038
orig_height = gdk_pixbuf_get_height (priv->map_pixbuf);
1040
/* Compute scaled width and height based on the extreme dimension */
1042
if ((double) width / orig_width > (double) height / orig_height)
1044
zoom = (double) width / (double) orig_width;
1048
zoom = (double) height / (double) orig_height;
1051
if (priv->zoom_state == E_MAP_ZOOMED_IN) zoom *= 2.0;
1052
height = (orig_height * zoom) + 0.5;
1053
width = (orig_width * zoom) + 0.5;
1055
/* Reallocate the pixbuf */
1057
if (priv->map_render_pixbuf) gdk_pixbuf_unref (priv->map_render_pixbuf);
1058
priv->map_render_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, /* No alpha */
1061
/* Scale the original map into the rendering pixbuf */
1063
if (width > 1 && height > 1)
1065
gdk_pixbuf_scale (priv->map_pixbuf, priv->map_render_pixbuf, 0, 0, /* Dest (x, y) */
1066
width, height, 0, 0, /* Offset (x, y) */
1067
zoom, zoom, /* Scale (x, y) */
1071
if (render_overlays)
1075
for (i = 0; i < priv->points->len; i++)
1077
point = g_ptr_array_index (priv->points, i);
1078
update_render_point (map, point);
1082
/* Compute image offsets with respect to window */
1084
set_scroll_area (map);
1088
/* Queues a repaint of the specified area in window coordinates */
1091
request_paint_area (EMap *view, GdkRectangle *area)
1096
if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (view)) ||
1097
!GTK_WIDGET_REALIZED (GTK_WIDGET (view))) return;
1100
if (!priv->map_render_pixbuf) return;
1102
width = MIN (area->width, E_MAP_GET_WIDTH (view));
1103
height = MIN (area->height, E_MAP_GET_HEIGHT (view));
1105
/* This satisfies paranoia. To be removed */
1107
if (priv->xofs + width > gdk_pixbuf_get_width (priv->map_render_pixbuf))
1108
width = gdk_pixbuf_get_width (priv->map_render_pixbuf) - priv->xofs;
1110
if (priv->yofs + height > gdk_pixbuf_get_height (priv->map_render_pixbuf))
1111
height = gdk_pixbuf_get_height (priv->map_render_pixbuf) - priv->yofs;
1113
/* We rely on the fast case always being the case, since we load and
1114
* preprocess the source pixbuf ourselves */
1116
if (gdk_pixbuf_get_colorspace (priv->map_render_pixbuf) == GDK_COLORSPACE_RGB && !gdk_pixbuf_get_has_alpha (priv->map_render_pixbuf) &&
1117
gdk_pixbuf_get_bits_per_sample (priv->map_render_pixbuf) == 8)
1122
rowstride = gdk_pixbuf_get_rowstride (priv->map_render_pixbuf);
1123
pixels = gdk_pixbuf_get_pixels (priv->map_render_pixbuf) + (area->y + priv->yofs) * rowstride + 3 * (area->x + priv->xofs);
1124
gdk_draw_rgb_image_dithalign (GTK_WIDGET (view)->window, GTK_WIDGET (view)->style->black_gc, area->x, area->y, width, height, GDK_RGB_DITHER_NORMAL, pixels, rowstride, 0, 0);
1129
g_print ("Doing hard redraw.\n");
1134
put_pixel_with_clipping (GdkPixbuf *pixbuf, gint x, gint y, guint rgba)
1137
gint rowstride, n_channels;
1138
guchar *pixels, *pixel;
1140
width = gdk_pixbuf_get_width (pixbuf);
1141
height = gdk_pixbuf_get_height (pixbuf);
1142
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
1143
n_channels = gdk_pixbuf_get_n_channels (pixbuf);
1144
pixels = gdk_pixbuf_get_pixels (pixbuf);
1146
if (x < 0 || x >= width || y < 0 || y >= height)
1149
pixel = pixels + (y * rowstride) + (x * n_channels);
1151
*pixel = (rgba >> 24);
1152
*(pixel + 1) = (rgba >> 16) & 0x000000ff;
1153
*(pixel + 2) = (rgba >> 8) & 0x000000ff;
1157
*(pixel + 3) = rgba & 0x000000ff;
1162
/* Redraw point in client pixbuf */
1165
update_render_point (EMap *map, EMapPoint *point)
1173
pb = priv->map_render_pixbuf;
1176
width = gdk_pixbuf_get_width (pb);
1177
height = gdk_pixbuf_get_height (pb);
1179
e_map_world_to_window (map, point->longitude, point->latitude, &px, &py);
1183
put_pixel_with_clipping (pb, px, py, point->rgba);
1184
put_pixel_with_clipping (pb, px - 1, py, point->rgba);
1185
put_pixel_with_clipping (pb, px + 1, py, point->rgba);
1186
put_pixel_with_clipping (pb, px, py - 1, point->rgba);
1187
put_pixel_with_clipping (pb, px, py + 1, point->rgba);
1189
put_pixel_with_clipping (pb, px - 2, py, 0x000000ff);
1190
put_pixel_with_clipping (pb, px + 2, py, 0x000000ff);
1191
put_pixel_with_clipping (pb, px, py - 2, 0x000000ff);
1192
put_pixel_with_clipping (pb, px, py + 2, 0x000000ff);
1193
put_pixel_with_clipping (pb, px - 1, py - 1, 0x000000ff);
1194
put_pixel_with_clipping (pb, px - 1, py + 1, 0x000000ff);
1195
put_pixel_with_clipping (pb, px + 1, py - 1, 0x000000ff);
1196
put_pixel_with_clipping (pb, px + 1, py + 1, 0x000000ff);
1200
/* Repaint point on X server */
1203
repaint_point (EMap *map, EMapPoint *point)
1209
if (!e_map_point_is_in_view (map, point)) return;
1212
e_map_world_to_window (map, point->longitude, point->latitude, &px, &py);
1214
area.x = (int) px - 2;
1215
area.y = (int) py - 2;
1218
request_paint_area (map, &area);
1223
center_at (EMap *map, int x, int y, gboolean scroll)
1226
int pb_width, pb_height,
1227
view_width, view_height;
1231
pb_width = E_MAP_GET_WIDTH (map);
1232
pb_height = E_MAP_GET_HEIGHT (map);
1234
view_width = GTK_WIDGET (map)->allocation.width;
1235
view_height = GTK_WIDGET (map)->allocation.height;
1237
x = CLAMP (x - (view_width / 2), 0, pb_width - view_width);
1238
y = CLAMP (y - (view_height / 2), 0, pb_height - view_height);
1240
if (scroll) scroll_to (map, x, y);
1250
smooth_center_at (EMap *map, int x, int y)
1253
int pb_width, pb_height,
1254
view_width, view_height;
1259
pb_width = E_MAP_GET_WIDTH (map);
1260
pb_height = E_MAP_GET_HEIGHT (map);
1262
view_width = GTK_WIDGET (map)->allocation.width;
1263
view_height = GTK_WIDGET (map)->allocation.height;
1265
x = CLAMP (x - (view_width / 2), 0, pb_width - view_width);
1266
y = CLAMP (y - (view_height / 2), 0, pb_height - view_height);
1270
if (priv->xofs == x && priv->yofs == y) break;
1272
dx = (x < priv->xofs) ? -1 : (x > priv->xofs) ? 1 : 0;
1273
dy = (y < priv->yofs) ? -1 : (y > priv->yofs) ? 1 : 0;
1275
scroll_to (map, priv->xofs + dx, priv->yofs + dy);
1280
/* Scrolls the view to the specified offsets. Does not perform range checking! */
1283
scroll_to (EMap *view, int x, int y)
1296
/* Compute offsets and check bounds */
1298
xofs = x - priv->xofs;
1299
yofs = y - priv->yofs;
1301
if (xofs == 0 && yofs == 0) return;
1306
if (!GTK_WIDGET_DRAWABLE (view)) return;
1308
width = GTK_WIDGET (view)->allocation.width;
1309
height = GTK_WIDGET (view)->allocation.height;
1311
if (abs (xofs) >= width || abs (yofs) >= height)
1318
area.height = height;
1320
request_paint_area (view, &area);
1324
window = GTK_WIDGET (view)->window;
1326
/* Copy the window area */
1328
src_x = xofs < 0 ? 0 : xofs;
1329
src_y = yofs < 0 ? 0 : yofs;
1330
dest_x = xofs < 0 ? -xofs : 0;
1331
dest_y = yofs < 0 ? -yofs : 0;
1333
gc = gdk_gc_new (window);
1334
gdk_gc_set_exposures (gc, TRUE);
1336
gdk_draw_drawable (window, gc, window, src_x, src_y, dest_x, dest_y, width - abs (xofs), height - abs (yofs));
1340
/* Add the scrolled-in region */
1346
r.x = xofs < 0 ? 0 : width - xofs;
1348
r.width = abs (xofs);
1351
request_paint_area (view, &r);
1359
r.y = yofs < 0 ? 0 : height - yofs;
1361
r.height = abs (yofs);
1363
request_paint_area (view, &r);
1366
/* Process graphics exposures */
1368
while ((event = gdk_event_get_graphics_expose (window)) != NULL)
1370
gtk_widget_event (GTK_WIDGET (view), event);
1372
if (event->expose.count == 0)
1374
gdk_event_free (event);
1378
gdk_event_free (event);
1383
static int divide_seq[] =
1385
/* Dividends for divisor of 2 */
1391
/* Dividends for divisor of 4 */
1397
/* Dividends for divisor of 8 */
1403
/* Dividends for divisor of 16 */
1407
1, 9, 5, 13, 3, 11, 7, 15,
1409
/* Dividends for divisor of 32 */
1413
1, 17, 9, 25, 5, 21, 13, 29, 3, 19,
1414
11, 27, 7, 23, 15, 31,
1416
/* Dividends for divisor of 64 */
1420
1, 33, 17, 49, 9, 41, 25, 57, 5, 37,
1421
21, 53, 13, 45, 29, 61, 3, 35, 19, 51,
1422
11, 43, 27, 59, 7, 39, 23, 55, 15, 47,
1425
/* Dividends for divisor of 128 */
1429
1, 65, 33, 97, 17, 81, 49, 113, 9, 73,
1430
41, 105, 25, 89, 57, 121, 5, 69, 37, 101,
1431
21, 85, 53, 117, 13, 77, 45, 109, 29, 93,
1432
61, 125, 3, 67, 35, 99, 19, 83, 51, 115,
1433
11, 75, 43, 107, 27, 91, 59, 123, 7, 71,
1434
39, 103, 23, 87, 55, 119, 15, 79, 47, 111,
1437
/* Dividends for divisor of 256 */
1441
1, 129, 65, 193, 33, 161, 97, 225, 17, 145,
1442
81, 209, 49, 177, 113, 241, 9, 137, 73, 201,
1443
41, 169, 105, 233, 25, 153, 89, 217, 57, 185,
1444
121, 249, 5, 133, 69, 197, 37, 165, 101, 229,
1445
21, 149, 85, 213, 53, 181, 117, 245, 13, 141,
1446
77, 205, 45, 173, 109, 237, 29, 157, 93, 221,
1447
61, 189, 125, 253, 3, 131, 67, 195, 35, 163,
1448
99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
1449
11, 139, 75, 203, 43, 171, 107, 235, 27, 155,
1450
91, 219, 59, 187, 123, 251, 7, 135, 71, 199,
1451
39, 167, 103, 231, 23, 151, 87, 215, 55, 183,
1452
119, 247, 15, 143, 79, 207, 47, 175, 111, 239,
1453
31, 159, 95, 223, 63, 191, 127, 255,
1468
blowup_window_area (GdkWindow *window, gint area_x, gint area_y, gint target_x, gint target_y, gint total_width, gint total_height, gfloat zoom_factor)
1471
AxisType strong_axis;
1472
gfloat axis_factor, axis_counter;
1474
gint divisor_width = 0, divisor_height = 0;
1475
gint divide_width_index, divide_height_index;
1476
gint area_width, area_height;
1481
/* Set up the GC we'll be using */
1483
gc = gdk_gc_new (window);
1484
gdk_gc_set_exposures (gc, FALSE);
1486
/* Get area constraints */
1488
gdk_drawable_get_size (window, &area_width, &area_height);
1490
/* Initialize area division array indexes */
1492
divide_width_index = divide_height_index = 0;
1494
/* Initialize axis counter */
1498
/* Find the strong axis (which is the basis for iteration) and the ratio
1499
* at which the other axis will be scaled.
1501
* Also determine how many lines to expand in one fell swoop, and store
1502
* this figure in zoom_chunk. */
1504
if (area_width > area_height)
1506
strong_axis = AXIS_X;
1507
axis_factor = (double) area_height / (double) area_width;
1508
zoom_chunk = MAX (1, area_width / 250);
1509
i = (area_width * (zoom_factor - 1.0)) / zoom_chunk;
1513
strong_axis = AXIS_Y;
1514
axis_factor = (double) area_width / (double) area_height;
1515
zoom_chunk = MAX (1, area_height / 250);
1516
i = (area_height * (zoom_factor - 1.0)) / zoom_chunk;
1519
/* Go, go, devil bunnies! Gogo devil bunnies! */
1523
/* Reset division sequence table indexes as necessary */
1525
if (!divide_seq[divide_width_index]) divide_width_index = 0;
1526
if (!divide_seq[divide_height_index]) divide_height_index = 0;
1528
/* Set new divisor if found in table */
1530
if (divide_seq[divide_width_index] < 0)
1531
divisor_width = abs (divide_seq[divide_width_index++]);
1532
if (divide_seq[divide_height_index] < 0)
1533
divisor_height = abs (divide_seq[divide_height_index++]);
1537
if (strong_axis == AXIS_X || axis_counter >= 1.0)
1539
line = ((divide_seq[divide_width_index] * area_width) / divisor_width) + 0.5;
1541
if ((line < target_x && target_x > area_width / 2) || (line > target_x && target_x > (area_width / 2) + zoom_chunk))
1545
for (j = 0; j < zoom_chunk - 1; j++)
1546
gdk_draw_drawable (window, gc, window, line, 0, line + j +1, 0, 1, area_height);
1548
gdk_draw_drawable (window, gc, window, zoom_chunk, 0, 0, 0, line, area_height);
1549
if (line > target_x) target_x -= zoom_chunk;
1555
for (j = 0; j < zoom_chunk - 1; j++)
1556
gdk_draw_drawable (window, gc, window, line - zoom_chunk, 0, line + j - (zoom_chunk - 1), 0, 1, area_height);
1558
gdk_draw_drawable (window, gc, window, line - zoom_chunk, 0, line, 0, area_width - line, area_height);
1559
if (line < target_x) target_x += zoom_chunk;
1563
if (strong_axis == AXIS_Y || axis_counter >= 1.0)
1567
line = ((divide_seq[divide_height_index] * area_height) / divisor_height) + 0.5;
1569
if ((line < target_y && target_y > area_height / 2) || (line > target_y && target_y > (area_height / 2) + zoom_chunk))
1573
for (j = 0; j < zoom_chunk - 1; j++)
1574
gdk_draw_drawable (window, gc, window, 0, line, 0, line + j + 1, area_width, 1);
1576
gdk_draw_drawable (window, gc, window, 0, zoom_chunk, 0, 0, area_width, line);
1577
if (line > target_y) target_y -= zoom_chunk;
1583
for (j = 0; j < zoom_chunk - 1; j++)
1584
gdk_draw_drawable (window, gc, window, 0, line - zoom_chunk, 0, line + j - (zoom_chunk - 1), area_width, 1);
1586
gdk_draw_drawable (window, gc, window, 0, line - zoom_chunk, 0, line, area_width, area_height - line);
1587
if (line < target_y) target_y += zoom_chunk;
1591
divide_width_index++;
1592
divide_height_index++;
1593
if (axis_counter >= 1.0) axis_counter -= 1.0;
1594
axis_counter += axis_factor;
1604
zoom_in_smooth (EMap *map)
1610
int win_width, win_height;
1611
int target_width, target_height;
1614
g_return_if_fail (map);
1615
g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (map)));
1619
area.width = GTK_WIDGET (map)->allocation.width;
1620
area.height = GTK_WIDGET (map)->allocation.height;
1623
window = GTK_WIDGET (map)->window;
1624
width = gdk_pixbuf_get_width (priv->map_render_pixbuf);
1625
height = gdk_pixbuf_get_height (priv->map_render_pixbuf);
1626
win_width = GTK_WIDGET (map)->allocation.width;
1627
win_height = GTK_WIDGET (map)->allocation.height;
1628
target_width = win_width / 4;
1629
target_height = win_height / 4;
1631
/* Center the target point as much as possible */
1633
e_map_world_to_window (map, priv->zoom_target_long, priv->zoom_target_lat, &x, &y);
1634
smooth_center_at (map, x + priv->xofs, y + priv->yofs);
1636
/* Render and paint a temporary map without overlays, so they don't get in
1637
* the way (look ugly) while zooming */
1639
update_render_pixbuf (map, GDK_INTERP_BILINEAR, FALSE);
1640
request_paint_area (map, &area);
1642
/* Find out where in the area we're going to zoom to */
1644
e_map_world_to_window (map, priv->zoom_target_long, priv->zoom_target_lat, &x, &y);
1646
/* Pre-render the zoomed-in map, so we can put it there quickly when the
1647
* blowup sequence ends */
1649
priv->zoom_state = E_MAP_ZOOMED_IN;
1650
update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE);
1654
blowup_window_area (window, priv->xofs, priv->yofs, x, y, width, height, 1.68);
1656
/* Set new scroll offsets and paint the zoomed map */
1658
e_map_world_to_window (map, priv->zoom_target_long, priv->zoom_target_lat, &x, &y);
1659
priv->xofs = CLAMP (priv->xofs + x - area.width / 2.0, 0, E_MAP_GET_WIDTH (map) - area.width);
1660
priv->yofs = CLAMP (priv->yofs + y - area.height / 2.0, 0, E_MAP_GET_HEIGHT (map) - area.height);
1662
request_paint_area (map, &area);
1677
area.width = GTK_WIDGET (map)->allocation.width;
1678
area.height = GTK_WIDGET (map)->allocation.height;
1680
priv->zoom_state = E_MAP_ZOOMED_IN;
1682
update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE);
1684
e_map_world_to_window (map, priv->zoom_target_long, priv->zoom_target_lat, &x, &y);
1685
priv->xofs = CLAMP (priv->xofs + x - area.width / 2.0, 0, E_MAP_GET_WIDTH (map) - area.width);
1686
priv->yofs = CLAMP (priv->yofs + y - area.height / 2.0, 0, E_MAP_GET_HEIGHT (map) - area.height);
1688
request_paint_area (map, &area);
1693
zoom_out (EMap *map)
1697
double longitude, latitude;
1704
area.width = GTK_WIDGET (map)->allocation.width;
1705
area.height = GTK_WIDGET (map)->allocation.height;
1707
/* Must be done before update_render_pixbuf() */
1709
e_map_window_to_world (map, area.width / 2, area.height / 2,
1710
&longitude, &latitude);
1712
priv->zoom_state = E_MAP_ZOOMED_OUT;
1713
update_render_pixbuf (map, GDK_INTERP_BILINEAR, TRUE);
1715
e_map_world_to_window (map, longitude, latitude, &x, &y);
1716
center_at (map, x + priv->xofs, y + priv->yofs, FALSE);
1717
/* request_paint_area (map, &area); */
1718
repaint_visible (map);
1729
g_signal_handler_block (G_OBJECT (priv->hadj), priv->hadj_signal_id);
1730
g_signal_handler_block (G_OBJECT (priv->vadj), priv->vadj_signal_id);
1732
if (priv->zoom_state == E_MAP_ZOOMING_IN)
1734
if (e_map_get_smooth_zoom (map)) zoom_in_smooth (map);
1737
else if (priv->zoom_state == E_MAP_ZOOMING_OUT)
1739
/* if (e_map_get_smooth_zoom(map)) zoom_out_smooth(map); */
1743
g_signal_handler_unblock (G_OBJECT (priv->hadj), priv->hadj_signal_id);
1744
g_signal_handler_unblock (G_OBJECT (priv->vadj), priv->vadj_signal_id);
1746
set_scroll_area(map);
1750
/* Callback used when an adjustment is changed */
1753
adjustment_changed_cb (GtkAdjustment *adj, gpointer data)
1758
view = E_MAP (data);
1761
scroll_to (view, priv->hadj->value, priv->vadj->value);
1766
set_scroll_area (EMap *view)
1772
if (!GTK_WIDGET_REALIZED (GTK_WIDGET (view))) return;
1773
if (!priv->hadj || !priv->vadj) return;
1775
/* Set scroll increments */
1777
priv->hadj->page_size = GTK_WIDGET (view)->allocation.width;
1778
priv->hadj->page_increment = GTK_WIDGET (view)->allocation.width / 2;
1779
priv->hadj->step_increment = SCROLL_STEP_SIZE;
1781
priv->vadj->page_size = GTK_WIDGET (view)->allocation.height;
1782
priv->vadj->page_increment = GTK_WIDGET (view)->allocation.height / 2;
1783
priv->vadj->step_increment = SCROLL_STEP_SIZE;
1785
/* Set scroll bounds and new offsets */
1787
priv->hadj->lower = 0;
1788
if (priv->map_render_pixbuf)
1789
priv->hadj->upper = gdk_pixbuf_get_width (priv->map_render_pixbuf);
1791
priv->vadj->lower = 0;
1792
if (priv->map_render_pixbuf)
1793
priv->vadj->upper = gdk_pixbuf_get_height (priv->map_render_pixbuf);
1795
gtk_signal_emit_by_name (GTK_OBJECT (priv->hadj), "changed");
1796
gtk_signal_emit_by_name (GTK_OBJECT (priv->vadj), "changed");
1798
priv->xofs = CLAMP (priv->xofs, 0, priv->hadj->upper - priv->hadj->page_size);
1799
priv->yofs = CLAMP (priv->yofs, 0, priv->vadj->upper - priv->vadj->page_size);
1801
if (priv->hadj->value != priv->xofs)
1803
priv->hadj->value = priv->xofs;
1805
g_signal_handler_block (G_OBJECT (priv->hadj), priv->hadj_signal_id);
1806
g_signal_emit_by_name (GTK_OBJECT (priv->hadj), "value_changed");
1807
g_signal_handler_unblock (G_OBJECT (priv->hadj), priv->hadj_signal_id);
1810
if (priv->vadj->value != priv->yofs)
1812
priv->vadj->value = priv->yofs;
1814
g_signal_handler_block (G_OBJECT (priv->vadj), priv->vadj_signal_id);
1815
g_signal_emit_by_name (GTK_OBJECT (priv->vadj), "value_changed");
1816
g_signal_handler_unblock (G_OBJECT (priv->vadj), priv->vadj_signal_id);