evolution-3.6.4/widgets/misc/e-map.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-map.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-map.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /*
   2  * Map widget.
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Authors:
  19  *		Hans Petter Jansson <hpj@ximian.com>
  20  *
  21  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  22  *
  23  */
  24 
  25 #ifdef HAVE_CONFIG_H
  26 #include <config.h>
  27 #endif
  28 
  29 #include <math.h>
  30 #include <stdlib.h>
  31 #include <gdk/gdkkeysyms.h>
  32 #include <glib/gi18n.h>
  33 
  34 #include "e-util/e-util-private.h"
  35 #include "e-util/e-util.h"
  36 
  37 #include "e-map.h"
  38 
  39 #define E_MAP_GET_PRIVATE(obj) \
  40 	(G_TYPE_INSTANCE_GET_PRIVATE \
  41 	((obj), E_TYPE_MAP, EMapPrivate))
  42 
  43 #define E_MAP_TWEEN_TIMEOUT_MSECS 25
  44 #define E_MAP_TWEEN_DURATION_MSECS 150
  45 
  46 /* Scroll step increment */
  47 
  48 #define SCROLL_STEP_SIZE 32
  49 
  50 /* */
  51 
  52 #define E_MAP_GET_WIDTH(map) gtk_adjustment_get_upper((map)->priv->hadjustment)
  53 #define E_MAP_GET_HEIGHT(map) gtk_adjustment_get_upper((map)->priv->vadjustment)
  54 
  55 /* Zoom state - keeps track of animation hacks */
  56 
  57 typedef enum
  58 {
  59 	E_MAP_ZOOMED_IN,
  60 	E_MAP_ZOOMED_OUT,
  61 	E_MAP_ZOOMING_IN,
  62 	E_MAP_ZOOMING_OUT
  63 }
  64 EMapZoomState;
  65 
  66 /* The Tween struct used for zooming */
  67 
  68 typedef struct _EMapTween EMapTween;
  69 
  70 struct _EMapTween {
  71 	guint start_time;
  72 	guint end_time;
  73 	gdouble longitude_offset;
  74 	gdouble latitude_offset;
  75 	gdouble zoom_factor;
  76 };
  77 
  78 /* Private part of the EMap structure */
  79 
  80 struct _EMapPrivate {
  81 	/* Pointer to map image */
  82 	GdkPixbuf *map_pixbuf;
  83 	cairo_surface_t *map_render_surface;
  84 
  85 	/* Settings */
  86 	gboolean frozen, smooth_zoom;
  87 
  88 	/* Adjustments for scrolling */
  89 	GtkAdjustment *hadjustment;
  90 	GtkAdjustment *vadjustment;
  91 
  92 	/* GtkScrollablePolicy needs to be checked when
  93 	 * driving the scrollable adjustment values */
  94 	guint hscroll_policy : 1;
  95 	guint vscroll_policy : 1;
  96 
  97 	/* Current scrolling offsets */
  98 	gint xofs, yofs;
  99 
 100 	/* Realtime zoom data */
 101 	EMapZoomState zoom_state;
 102 	gdouble zoom_target_long, zoom_target_lat;
 103 
 104 	/* Dots */
 105 	GPtrArray *points;
 106 
 107 	/* Tweens */
 108 	GSList *tweens;
 109 	GTimer *timer;
 110 	guint timer_current_ms;
 111 	guint tween_id;
 112 };
 113 
 114 /* Properties */
 115 
 116 enum {
 117 	PROP_0,
 118 
 119 	/* For scrollable interface */
 120 	PROP_HADJUSTMENT,
 121 	PROP_VADJUSTMENT,
 122 	PROP_HSCROLL_POLICY,
 123 	PROP_VSCROLL_POLICY
 124 };
 125 
 126 /* Internal prototypes */
 127 
 128 static void update_render_surface (EMap *map, gboolean render_overlays);
 129 static void set_scroll_area (EMap *map, gint width, gint height);
 130 static void center_at (EMap *map, gdouble longitude, gdouble latitude);
 131 static void scroll_to (EMap *map, gint x, gint y);
 132 static gint load_map_background (EMap *map, gchar *name);
 133 static void update_and_paint (EMap *map);
 134 static void update_render_point (EMap *map, EMapPoint *point);
 135 static void repaint_point (EMap *map, EMapPoint *point);
 136 
 137 /* ------ *
 138  * Tweens *
 139  * ------ */
 140 
 141 static gboolean
 142 e_map_is_tweening (EMap *map)
 143 {
 144 	return map->priv->timer != NULL;
 145 }
 146 
 147 static void
 148 e_map_stop_tweening (EMap *map)
 149 {
 150 	g_assert (map->priv->tweens == NULL);
 151 
 152 	if (!e_map_is_tweening (map))
 153 		return;
 154 
 155 	g_timer_destroy (map->priv->timer);
 156 	map->priv->timer = NULL;
 157 	g_source_remove (map->priv->tween_id);
 158 	map->priv->tween_id = 0;
 159 }
 160 
 161 static void
 162 e_map_tween_destroy (EMap *map,
 163                      EMapTween *tween)
 164 {
 165 	map->priv->tweens = g_slist_remove (map->priv->tweens, tween);
 166 	g_slice_free (EMapTween, tween);
 167 
 168 	if (map->priv->tweens == NULL)
 169 		e_map_stop_tweening (map);
 170 }
 171 
 172 static gboolean
 173 e_map_do_tween_cb (gpointer data)
 174 {
 175 	EMap *map = data;
 176 	GSList *walk;
 177 
 178 	map->priv->timer_current_ms =
 179 		g_timer_elapsed (map->priv->timer, NULL) * 1000;
 180 	gtk_widget_queue_draw (GTK_WIDGET (map));
 181 
 182 	/* Can't use for loop here, because we need to advance 
 183 	 * the list before deleting.
 184 	 */
 185 	walk = map->priv->tweens;
 186 	while (walk)
 187 	{
 188 		EMapTween *tween = walk->data;
 189 
 190 		walk = walk->next;
 191 
 192 		if (tween->end_time <= map->priv->timer_current_ms)
 193 			e_map_tween_destroy (map, tween);
 194 	}
 195 
 196 	return TRUE;
 197 }
 198 
 199 static void
 200 e_map_start_tweening (EMap *map)
 201 {
 202 	if (e_map_is_tweening (map))
 203 		return;
 204 
 205 	map->priv->timer = g_timer_new ();
 206 	map->priv->timer_current_ms = 0;
 207 	map->priv->tween_id = g_timeout_add (
 208 		E_MAP_TWEEN_TIMEOUT_MSECS, e_map_do_tween_cb, map);
 209 	g_timer_start (map->priv->timer);
 210 }
 211 
 212 static void
 213 e_map_tween_new (EMap *map,
 214                  guint msecs,
 215                  gdouble longitude_offset,
 216                  gdouble latitude_offset,
 217                  gdouble zoom_factor)
 218 {
 219 	EMapTween *tween;
 220 
 221 	if (!map->priv->smooth_zoom)
 222 		return;
 223 
 224 	e_map_start_tweening (map);
 225 
 226 	tween = g_slice_new (EMapTween);
 227 
 228 	tween->start_time = map->priv->timer_current_ms;
 229 	tween->end_time = tween->start_time + msecs;
 230 	tween->longitude_offset = longitude_offset;
 231 	tween->latitude_offset = latitude_offset;
 232 	tween->zoom_factor = zoom_factor;
 233 
 234 	map->priv->tweens = g_slist_prepend (map->priv->tweens, tween);
 235 
 236 	gtk_widget_queue_draw (GTK_WIDGET (map));
 237 }
 238 
 239 G_DEFINE_TYPE_WITH_CODE (
 240 	EMap,
 241 	e_map,
 242 	GTK_TYPE_WIDGET,
 243 	G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
 244 
 245 static void
 246 e_map_get_current_location (EMap *map,
 247                             gdouble *longitude,
 248                             gdouble *latitude)
 249 {
 250 	GtkAllocation allocation;
 251 
 252 	gtk_widget_get_allocation (GTK_WIDGET (map), &allocation);
 253 
 254 	e_map_window_to_world (
 255 		map, allocation.width / 2.0,
 256 		allocation.height / 2.0,
 257 		longitude, latitude);
 258 }
 259 
 260 static void
 261 e_map_world_to_render_surface (EMap *map,
 262                                gdouble world_longitude,
 263                                gdouble world_latitude,
 264                                gdouble *win_x,
 265                                gdouble *win_y)
 266 {
 267 	gint width, height;
 268 
 269 	width = E_MAP_GET_WIDTH (map);
 270 	height = E_MAP_GET_HEIGHT (map);
 271 
 272 	*win_x = (width / 2.0 + (width / 2.0) * world_longitude / 180.0);
 273 	*win_y = (height / 2.0 - (height / 2.0) * world_latitude / 90.0);
 274 }
 275 
 276 static void
 277 e_map_tween_new_from (EMap *map,
 278                       guint msecs,
 279                       gdouble longitude,
 280                       gdouble latitude,
 281                       gdouble zoom)
 282 {
 283 	gdouble current_longitude, current_latitude;
 284 
 285 	e_map_get_current_location (
 286 		map, &current_longitude, &current_latitude);
 287 
 288 	e_map_tween_new (
 289 		map, msecs,
 290 		longitude - current_longitude,
 291 		latitude - current_latitude,
 292 		zoom / e_map_get_magnification (map));
 293 }
 294 
 295 static gdouble
 296 e_map_get_tween_effect (EMap *map,
 297                         EMapTween *tween)
 298 {
 299 	gdouble elapsed;
 300 
 301 	elapsed = (gdouble)
 302 		(map->priv->timer_current_ms - tween->start_time) /
 303 		tween->end_time;
 304 
 305 	return MAX (0.0, 1.0 - elapsed);
 306 }
 307 
 308 static void
 309 e_map_apply_tween (EMapTween *tween,
 310                    gdouble effect,
 311                    gdouble *longitude,
 312                    gdouble *latitude,
 313                    gdouble *zoom)
 314 {
 315 	*zoom *= pow (tween->zoom_factor, effect);
 316 	*longitude += tween->longitude_offset * effect;
 317 	*latitude += tween->latitude_offset * effect;
 318 }
 319 
 320 static void
 321 e_map_tweens_compute_matrix (EMap *map,
 322                              cairo_matrix_t *matrix)
 323 {
 324 	GSList *walk;
 325 	gdouble zoom, x, y, latitude, longitude, effect;
 326 	GtkAllocation allocation;
 327 
 328 	if (!e_map_is_tweening (map)) {
 329 		cairo_matrix_init_translate (
 330 			matrix, -map->priv->xofs, -map->priv->yofs);
 331 		return;
 332 	}
 333 
 334 	e_map_get_current_location (map, &longitude, &latitude);
 335 	zoom = 1.0;
 336 
 337 	for (walk = map->priv->tweens; walk; walk = walk->next) {
 338 		EMapTween *tween = walk->data;
 339 
 340 		effect = e_map_get_tween_effect (map, tween);
 341 		e_map_apply_tween (tween, effect, &longitude, &latitude, &zoom);
 342 	}
 343 
 344 	gtk_widget_get_allocation (GTK_WIDGET (map), &allocation);
 345 	cairo_matrix_init_translate (
 346 		matrix,
 347 		allocation.width / 2.0,
 348 		allocation.height / 2.0);
 349 
 350 	e_map_world_to_render_surface (map, longitude, latitude, &x, &y);
 351 	cairo_matrix_scale (matrix, zoom, zoom);
 352 	cairo_matrix_translate (matrix, -x, -y);
 353 }
 354 
 355 /* GtkScrollable implementation */
 356 
 357 static void
 358 e_map_adjustment_changed (GtkAdjustment *adjustment,
 359                           EMap *map)
 360 {
 361 	EMapPrivate *priv = map->priv;
 362 
 363 	if (gtk_widget_get_realized (GTK_WIDGET (map))) {
 364 		gint hadj_value;
 365 		gint vadj_value;
 366 
 367 		hadj_value = gtk_adjustment_get_value (priv->hadjustment);
 368 		vadj_value = gtk_adjustment_get_value (priv->vadjustment);
 369 
 370 		scroll_to (map, hadj_value, vadj_value);
 371 	}
 372 }
 373 
 374 static void
 375 e_map_set_hadjustment_values (EMap *map)
 376 {
 377 	GtkAllocation  allocation;
 378 	EMapPrivate *priv = map->priv;
 379 	GtkAdjustment *adj = priv->hadjustment;
 380 	gdouble old_value;
 381 	gdouble new_value;
 382 	gdouble new_upper;
 383 
 384 	gtk_widget_get_allocation (GTK_WIDGET (map), &allocation);
 385 
 386 	old_value = gtk_adjustment_get_value (adj);
 387 	new_upper = MAX (allocation.width, gdk_pixbuf_get_width (priv->map_pixbuf));
 388 
 389 	g_object_set (
 390 		adj,
 391 		"lower", 0.0,
 392 		"upper", new_upper,
 393 		"page-size", (gdouble) allocation.height,
 394 		"step-increment", allocation.height * 0.1,
 395 		"page-increment", allocation.height * 0.9,
 396 		NULL);
 397 
 398 	new_value = CLAMP (old_value, 0, new_upper - allocation.width);
 399 	if (new_value != old_value)
 400 		gtk_adjustment_set_value (adj, new_value);
 401 }
 402 
 403 static void
 404 e_map_set_vadjustment_values (EMap *map)
 405 {
 406 	GtkAllocation  allocation;
 407 	EMapPrivate *priv = map->priv;
 408 	GtkAdjustment *adj = priv->vadjustment;
 409 	gdouble old_value;
 410 	gdouble new_value;
 411 	gdouble new_upper;
 412 
 413 	gtk_widget_get_allocation (GTK_WIDGET (map), &allocation);
 414 
 415 	old_value = gtk_adjustment_get_value (adj);
 416 	new_upper = MAX (allocation.height, gdk_pixbuf_get_height (priv->map_pixbuf));
 417 
 418 	g_object_set (
 419 		adj,
 420 		"lower", 0.0,
 421 		"upper", new_upper,
 422 		"page-size", (gdouble) allocation.height,
 423 		"step-increment", allocation.height * 0.1,
 424 		"page-increment", allocation.height * 0.9,
 425 		NULL);
 426 
 427 	new_value = CLAMP (old_value, 0, new_upper - allocation.height);
 428 	if (new_value != old_value)
 429 		gtk_adjustment_set_value (adj, new_value);
 430 }
 431 
 432 static void
 433 e_map_set_hadjustment (EMap *map,
 434                        GtkAdjustment *adjustment)
 435 {
 436 	EMapPrivate *priv = map->priv;
 437 
 438 	if (adjustment && priv->hadjustment == adjustment)
 439 		return;
 440 
 441 	if (priv->hadjustment != NULL) {
 442 		g_signal_handlers_disconnect_matched (
 443 			priv->hadjustment, G_SIGNAL_MATCH_DATA,
 444 			0, 0, NULL, NULL, map);
 445 		g_object_unref (priv->hadjustment);
 446 	}
 447 
 448 	if (!adjustment)
 449 		adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
 450 
 451 	g_signal_connect (
 452 		adjustment, "value-changed",
 453 		G_CALLBACK (e_map_adjustment_changed), map);
 454 	priv->hadjustment = g_object_ref_sink (adjustment);
 455 	e_map_set_hadjustment_values (map);
 456 
 457 	g_object_notify (G_OBJECT (map), "hadjustment");
 458 }
 459 
 460 static void
 461 e_map_set_vadjustment (EMap *map,
 462                        GtkAdjustment *adjustment)
 463 {
 464 	EMapPrivate *priv = map->priv;
 465 
 466 	if (adjustment && priv->vadjustment == adjustment)
 467 		return;
 468 
 469 	if (priv->vadjustment != NULL) {
 470 		g_signal_handlers_disconnect_matched (
 471 			priv->vadjustment, G_SIGNAL_MATCH_DATA,
 472 			0, 0, NULL, NULL, map);
 473 		g_object_unref (priv->vadjustment);
 474 	}
 475 
 476 	if (!adjustment)
 477 		adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
 478 
 479 	g_signal_connect (
 480 		adjustment, "value-changed",
 481 		G_CALLBACK (e_map_adjustment_changed), map);
 482 	priv->vadjustment = g_object_ref_sink (adjustment);
 483 	e_map_set_vadjustment_values (map);
 484 
 485 	g_object_notify (G_OBJECT (map), "vadjustment");
 486 }
 487 
 488 /* ----------------- *
 489  * Widget management *
 490  * ----------------- */
 491 
 492 static void
 493 e_map_set_property (GObject *object,
 494                     guint property_id,
 495                     const GValue *value,
 496                     GParamSpec *pspec)
 497 {
 498 	EMap *map;
 499 
 500 	map = E_MAP (object);
 501 
 502 	switch (property_id) {
 503 	case PROP_HADJUSTMENT:
 504 		e_map_set_hadjustment (map, g_value_get_object (value));
 505 		break;
 506 	case PROP_VADJUSTMENT:
 507 		e_map_set_vadjustment (map, g_value_get_object (value));
 508 		break;
 509 	case PROP_HSCROLL_POLICY:
 510 		map->priv->hscroll_policy = g_value_get_enum (value);
 511 		gtk_widget_queue_resize (GTK_WIDGET (map));
 512 		break;
 513 	case PROP_VSCROLL_POLICY:
 514 		map->priv->vscroll_policy = g_value_get_enum (value);
 515 		gtk_widget_queue_resize (GTK_WIDGET (map));
 516 		break;
 517 
 518 	default:
 519 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 520 		break;
 521 	}
 522 }
 523 
 524 static void
 525 e_map_get_property (GObject *object,
 526                     guint property_id,
 527                     GValue *value,
 528                     GParamSpec *pspec)
 529 {
 530 	EMap *map;
 531 
 532 	map = E_MAP (object);
 533 
 534 	switch (property_id) {
 535 	case PROP_HADJUSTMENT:
 536 		g_value_set_object (value, map->priv->hadjustment);
 537 		break;
 538 	case PROP_VADJUSTMENT:
 539 		g_value_set_object (value, map->priv->vadjustment);
 540 		break;
 541 	case PROP_HSCROLL_POLICY:
 542 		g_value_set_enum (value, map->priv->hscroll_policy);
 543 		break;
 544 	case PROP_VSCROLL_POLICY:
 545 		g_value_set_enum (value, map->priv->vscroll_policy);
 546 		break;
 547 
 548 	default:
 549 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 550 		break;
 551 	}
 552 }
 553 
 554 static void
 555 e_map_finalize (GObject *object)
 556 {
 557 	EMap *map;
 558 
 559 	map = E_MAP (object);
 560 
 561 	while (map->priv->tweens)
 562 		e_map_tween_destroy (map, map->priv->tweens->data);
 563 	e_map_stop_tweening (map);
 564 
 565 	if (map->priv->map_pixbuf) {
 566 		g_object_unref (map->priv->map_pixbuf);
 567 		map->priv->map_pixbuf = NULL;
 568 	}
 569 
 570 	/* gone in unrealize */
 571 	g_assert (map->priv->map_render_surface == NULL);
 572 
 573 	G_OBJECT_CLASS (e_map_parent_class)->finalize (object);
 574 }
 575 
 576 static void
 577 e_map_realize (GtkWidget *widget)
 578 {
 579 	GtkAllocation allocation;
 580 	GdkWindowAttr attr;
 581 	GdkWindow *window;
 582 	GtkStyle *style;
 583 	gint attr_mask;
 584 
 585 	g_return_if_fail (widget != NULL);
 586 	g_return_if_fail (E_IS_MAP (widget));
 587 
 588 	gtk_widget_set_realized (widget, TRUE);
 589 
 590 	gtk_widget_get_allocation (widget, &allocation);
 591 
 592 	attr.window_type = GDK_WINDOW_CHILD;
 593 	attr.x = allocation.x;
 594 	attr.y = allocation.y;
 595 	attr.width = allocation.width;
 596 	attr.height = allocation.height;
 597 	attr.wclass = GDK_INPUT_OUTPUT;
 598 	attr.visual = gtk_widget_get_visual (widget);
 599 	attr.event_mask = gtk_widget_get_events (widget) |
 600 	  GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK |
 601 	  GDK_POINTER_MOTION_MASK;
 602 
 603 	attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
 604 
 605 	window = gdk_window_new (
 606 		gtk_widget_get_parent_window (widget), &attr, attr_mask);
 607 	gtk_widget_set_window (widget, window);
 608 	gdk_window_set_user_data (window, widget);
 609 
 610 	style = gtk_widget_get_style (widget);
 611 	style = gtk_style_attach (style, window);
 612 	gtk_widget_set_style (widget, style);
 613 
 614 	update_render_surface (E_MAP (widget), TRUE);
 615 }
 616 
 617 static void
 618 e_map_unrealize (GtkWidget *widget)
 619 {
 620 	EMap *map = E_MAP (widget);
 621 
 622 	cairo_surface_destroy (map->priv->map_render_surface);
 623 	map->priv->map_render_surface = NULL;
 624 
 625 	if (GTK_WIDGET_CLASS (e_map_parent_class)->unrealize)
 626 		(*GTK_WIDGET_CLASS (e_map_parent_class)->unrealize) (widget);
 627 }
 628 
 629 static void
 630 e_map_get_preferred_width (GtkWidget *widget,
 631                            gint *minimum,
 632                            gint *natural)
 633 {
 634 	EMap *map;
 635 
 636 	g_return_if_fail (widget != NULL);
 637 	g_return_if_fail (E_IS_MAP (widget));
 638 
 639 	map = E_MAP (widget);
 640 
 641 	/* TODO: Put real sizes here. */
 642 
 643 	*minimum = *natural = gdk_pixbuf_get_width (map->priv->map_pixbuf);
 644 }
 645 
 646 static void
 647 e_map_get_preferred_height (GtkWidget *widget,
 648                             gint *minimum,
 649                             gint *natural)
 650 {
 651 	EMap *view;
 652 	EMapPrivate *priv;
 653 
 654 	g_return_if_fail (widget != NULL);
 655 	g_return_if_fail (E_IS_MAP (widget));
 656 
 657 	view = E_MAP (widget);
 658 	priv = view->priv;
 659 
 660 	/* TODO: Put real sizes here. */
 661 
 662 	*minimum = *natural = gdk_pixbuf_get_height (priv->map_pixbuf);
 663 }
 664 
 665 static void
 666 e_map_size_allocate (GtkWidget *widget,
 667                      GtkAllocation *allocation)
 668 {
 669 	EMap *map;
 670 
 671 	g_return_if_fail (widget != NULL);
 672 	g_return_if_fail (E_IS_MAP (widget));
 673 	g_return_if_fail (allocation != NULL);
 674 
 675 	map = E_MAP (widget);
 676 
 677 	/* Resize the window */
 678 
 679 	gtk_widget_set_allocation (widget, allocation);
 680 
 681 	if (gtk_widget_get_realized (widget)) {
 682 		GdkWindow *window;
 683 
 684 		window = gtk_widget_get_window (widget);
 685 
 686 		gdk_window_move_resize (
 687 			window, allocation->x, allocation->y,
 688 			allocation->width, allocation->height);
 689 
 690 		gtk_widget_queue_draw (widget);
 691 	}
 692 
 693 	update_render_surface (map, TRUE);
 694 }
 695 
 696 static gboolean
 697 e_map_draw (GtkWidget *widget,
 698             cairo_t *cr)
 699 {
 700 	EMap *map;
 701 	cairo_matrix_t matrix;
 702 
 703 	if (!gtk_widget_is_drawable (widget))
 704 		return FALSE;
 705 
 706 	map = E_MAP (widget);
 707 
 708 	cairo_save (cr);
 709 
 710 	e_map_tweens_compute_matrix (map, &matrix);
 711 	cairo_transform (cr, &matrix);
 712 
 713 	cairo_set_source_surface (cr, map->priv->map_render_surface, 0, 0);
 714 	cairo_paint (cr);
 715 
 716 	cairo_restore (cr);
 717 
 718 	return FALSE;
 719 }
 720 
 721 static gint
 722 e_map_button_press (GtkWidget *widget,
 723                     GdkEventButton *event)
 724 {
 725 	if (!gtk_widget_has_focus (widget)) gtk_widget_grab_focus (widget);
 726 		return TRUE;
 727 }
 728 
 729 static gint
 730 e_map_button_release (GtkWidget *widget,
 731                       GdkEventButton *event)
 732 {
 733 	if (event->button != 1) return FALSE;
 734 
 735 	gdk_pointer_ungrab (event->time);
 736 	return TRUE;
 737 }
 738 
 739 static gint
 740 e_map_motion (GtkWidget *widget,
 741               GdkEventMotion *event)
 742 {
 743 	return FALSE;
 744 }
 745 
 746 static gint
 747 e_map_key_press (GtkWidget *widget,
 748                  GdkEventKey *event)
 749 {
 750 	EMap *map;
 751 	gboolean do_scroll;
 752 	gint xofs, yofs;
 753 
 754 	map = E_MAP (widget);
 755 
 756 	switch (event->keyval)
 757 	{
 758 		case GDK_KEY_Up:
 759 			do_scroll = TRUE;
 760 			xofs = 0;
 761 			yofs = -SCROLL_STEP_SIZE;
 762 			break;
 763 
 764 		case GDK_KEY_Down:
 765 			do_scroll = TRUE;
 766 			xofs = 0;
 767 			yofs = SCROLL_STEP_SIZE;
 768 			break;
 769 
 770 		case GDK_KEY_Left:
 771 			do_scroll = TRUE;
 772 			xofs = -SCROLL_STEP_SIZE;
 773 			yofs = 0;
 774 			break;
 775 
 776 		case GDK_KEY_Right:
 777 			do_scroll = TRUE;
 778 			xofs = SCROLL_STEP_SIZE;
 779 			yofs = 0;
 780 			break;
 781 
 782 		default:
 783 			return FALSE;
 784 	}
 785 
 786 	if (do_scroll) {
 787 		gint page_size;
 788 		gint upper;
 789 		gint x, y;
 790 
 791 		page_size = gtk_adjustment_get_page_size (map->priv->hadjustment);
 792 		upper = gtk_adjustment_get_upper (map->priv->hadjustment);
 793 		x = CLAMP (map->priv->xofs + xofs, 0, upper - page_size);
 794 
 795 		page_size = gtk_adjustment_get_page_size (map->priv->vadjustment);
 796 		upper = gtk_adjustment_get_upper (map->priv->vadjustment);
 797 		y = CLAMP (map->priv->yofs + yofs, 0, upper - page_size);
 798 
 799 		scroll_to (map, x, y);
 800 
 801 		gtk_adjustment_set_value (map->priv->hadjustment, x);
 802 		gtk_adjustment_set_value (map->priv->vadjustment, y);
 803 	}
 804 
 805 	return TRUE;
 806 }
 807 
 808 static void
 809 e_map_class_init (EMapClass *class)
 810 {
 811 	GObjectClass *object_class;
 812 	GtkWidgetClass *widget_class;
 813 
 814 	g_type_class_add_private (class, sizeof (EMapPrivate));
 815 
 816 	object_class = G_OBJECT_CLASS (class);
 817 	object_class->set_property = e_map_set_property;
 818 	object_class->get_property = e_map_get_property;
 819 	object_class->finalize = e_map_finalize;
 820 
 821 	/* Scrollable interface properties */
 822 	g_object_class_override_property (
 823 		object_class, PROP_HADJUSTMENT, "hadjustment");
 824 	g_object_class_override_property (
 825 		object_class, PROP_VADJUSTMENT, "vadjustment");
 826 	g_object_class_override_property (
 827 		object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
 828 	g_object_class_override_property (
 829 		object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
 830 
 831 	widget_class = GTK_WIDGET_CLASS (class);
 832 	widget_class->realize = e_map_realize;
 833 	widget_class->unrealize = e_map_unrealize;
 834 	widget_class->get_preferred_height = e_map_get_preferred_height;
 835 	widget_class->get_preferred_width = e_map_get_preferred_width;
 836 	widget_class->size_allocate = e_map_size_allocate;
 837 	widget_class->draw = e_map_draw;
 838 	widget_class->button_press_event = e_map_button_press;
 839 	widget_class->button_release_event = e_map_button_release;
 840 	widget_class->motion_notify_event = e_map_motion;
 841 	widget_class->key_press_event = e_map_key_press;
 842 }
 843 
 844 static void
 845 e_map_init (EMap *map)
 846 {
 847 	GtkWidget *widget;
 848 	gchar *map_file_name;
 849 
 850 	map_file_name = g_build_filename (
 851 		EVOLUTION_IMAGESDIR, "world_map-960.png", NULL);
 852 
 853 	widget = GTK_WIDGET (map);
 854 
 855 	map->priv = E_MAP_GET_PRIVATE (map);
 856 
 857 	load_map_background (map, map_file_name);
 858 	g_free (map_file_name);
 859 	map->priv->frozen = FALSE;
 860 	map->priv->smooth_zoom = TRUE;
 861 	map->priv->zoom_state = E_MAP_ZOOMED_OUT;
 862 	map->priv->points = g_ptr_array_new ();
 863 
 864 	gtk_widget_set_can_focus (widget, TRUE);
 865 	gtk_widget_set_has_window (widget, TRUE);
 866 }
 867 
 868 /* ---------------- *
 869  * Widget interface *
 870  * ---------------- */
 871 
 872 /**
 873  * e_map_new:
 874  * @void:
 875  *
 876  * Creates a new empty map widget.
 877  *
 878  * Return value: A newly-created map widget.
 879  **/
 880 
 881 EMap *
 882 e_map_new (void)
 883 {
 884 	GtkWidget *widget;
 885 	AtkObject *a11y;
 886 
 887 	widget = g_object_new (E_TYPE_MAP, NULL);
 888 	a11y = gtk_widget_get_accessible (widget);
 889 	atk_object_set_name (a11y, _("World Map"));
 890 	atk_object_set_role (a11y, ATK_ROLE_IMAGE);
 891 	atk_object_set_description (
 892 		a11y, _("Mouse-based interactive map widget for selecting "
 893 		"timezone. Keyboard users should instead select the timezone "
 894 		"from the drop-down combination box below."));
 895 	return (E_MAP (widget));
 896 }
 897 
 898 /* --- Coordinate translation --- */
 899 
 900 /* These functions translate coordinates between longitude/latitude and
 901  * the image x/y offsets, using the equidistant cylindrical projection.
 902  *
 903  * Longitude E <-180, 180]
 904  * Latitude  E <-90, 90]   */
 905 
 906 void
 907 e_map_window_to_world (EMap *map,
 908                        gdouble win_x,
 909                        gdouble win_y,
 910                        gdouble *world_longitude,
 911                        gdouble *world_latitude)
 912 {
 913 	gint width, height;
 914 
 915 	g_return_if_fail (map);
 916 
 917 	g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (map)));
 918 
 919 	width = E_MAP_GET_WIDTH (map);
 920 	height = E_MAP_GET_HEIGHT (map);
 921 
 922 	*world_longitude = (win_x + map->priv->xofs - (gdouble) width / 2.0) /
 923 		((gdouble) width / 2.0) * 180.0;
 924 	*world_latitude = ((gdouble) height / 2.0 - win_y - map->priv->yofs) /
 925 		((gdouble) height / 2.0) * 90.0;
 926 }
 927 
 928 void
 929 e_map_world_to_window (EMap *map,
 930                        gdouble world_longitude,
 931                        gdouble world_latitude,
 932                        gdouble *win_x,
 933                        gdouble *win_y)
 934 {
 935 	g_return_if_fail (E_IS_MAP (map));
 936 	g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (map)));
 937 	g_return_if_fail (world_longitude >= -180.0 && world_longitude <= 180.0);
 938 	g_return_if_fail (world_latitude >= -90.0 && world_latitude <= 90.0);
 939 
 940 	e_map_world_to_render_surface (
 941 		map, world_longitude, world_latitude, win_x, win_y);
 942 
 943 	*win_x -= map->priv->xofs;
 944 	*win_y -= map->priv->yofs;
 945 }
 946 
 947 /* --- Zoom --- */
 948 
 949 gdouble
 950 e_map_get_magnification (EMap *map)
 951 {
 952 	if (map->priv->zoom_state == E_MAP_ZOOMED_IN) return 2.0;
 953 	else return 1.0;
 954 }
 955 
 956 static void
 957 e_map_set_zoom (EMap *map,
 958                 EMapZoomState zoom)
 959 {
 960 	if (map->priv->zoom_state == zoom)
 961 		return;
 962 
 963 	map->priv->zoom_state = zoom;
 964 	update_render_surface (map, TRUE);
 965 	gtk_widget_queue_draw (GTK_WIDGET (map));
 966 }
 967 
 968 void
 969 e_map_zoom_to_location (EMap *map,
 970                         gdouble longitude,
 971                         gdouble latitude)
 972 {
 973 	gdouble prevlong, prevlat;
 974 	gdouble prevzoom;
 975 
 976 	g_return_if_fail (map);
 977 	g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (map)));
 978 
 979 	e_map_get_current_location (map, &prevlong, &prevlat);
 980 	prevzoom = e_map_get_magnification (map);
 981 
 982 	e_map_set_zoom (map, E_MAP_ZOOMED_IN);
 983 	center_at (map, longitude, latitude);
 984 
 985 	e_map_tween_new_from (
 986 		map, E_MAP_TWEEN_DURATION_MSECS,
 987 		prevlong, prevlat, prevzoom);
 988 }
 989 
 990 void
 991 e_map_zoom_out (EMap *map)
 992 {
 993 	gdouble longitude, latitude;
 994 	gdouble prevzoom;
 995 
 996 	g_return_if_fail (map);
 997 	g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (map)));
 998 
 999 	e_map_get_current_location (map, &longitude, &latitude);
1000 	prevzoom = e_map_get_magnification (map);
1001 	e_map_set_zoom (map, E_MAP_ZOOMED_OUT);
1002 	center_at (map, longitude, latitude);
1003 
1004 	e_map_tween_new_from (
1005 		map, E_MAP_TWEEN_DURATION_MSECS,
1006 		longitude, latitude, prevzoom);
1007 }
1008 
1009 void
1010 e_map_set_smooth_zoom (EMap *map,
1011                        gboolean state)
1012 {
1013 	((EMapPrivate *) map->priv)->smooth_zoom = state;
1014 }
1015 
1016 gboolean
1017 e_map_get_smooth_zoom (EMap *map)
1018 {
1019 	return (((EMapPrivate *) map->priv)->smooth_zoom);
1020 }
1021 
1022 void
1023 e_map_freeze (EMap *map)
1024 {
1025 	((EMapPrivate *) map->priv)->frozen = TRUE;
1026 }
1027 
1028 void
1029 e_map_thaw (EMap *map)
1030 {
1031 	((EMapPrivate *) map->priv)->frozen = FALSE;
1032 	update_and_paint (map);
1033 }
1034 
1035 /* --- Point manipulation --- */
1036 
1037 EMapPoint *
1038 e_map_add_point (EMap *map,
1039                  gchar *name,
1040                  gdouble longitude,
1041                  gdouble latitude,
1042                  guint32 color_rgba)
1043 {
1044 	EMapPoint *point;
1045 
1046 	point = g_new0 (EMapPoint, 1);
1047 
1048 	point->name = name;  /* Can be NULL */
1049 	point->longitude = longitude;
1050 	point->latitude = latitude;
1051 	point->rgba = color_rgba;
1052 
1053 	g_ptr_array_add (map->priv->points, (gpointer) point);
1054 
1055 	if (!map->priv->frozen)
1056 	{
1057 		update_render_point (map, point);
1058 		repaint_point (map, point);
1059 	}
1060 
1061 	return point;
1062 }
1063 
1064 void
1065 e_map_remove_point (EMap *map,
1066                     EMapPoint *point)
1067 {
1068 	g_ptr_array_remove (map->priv->points, point);
1069 
1070 	if (!((EMapPrivate *) map->priv)->frozen)
1071 	{
1072 		/* FIXME: Re-scaling the whole pixbuf is more than a little
1073 		 * overkill when just one point is removed */
1074 
1075 		update_render_surface (map, TRUE);
1076 		repaint_point (map, point);
1077 	}
1078 
1079 	g_free (point);
1080 }
1081 
1082 void
1083 e_map_point_get_location (EMapPoint *point,
1084                           gdouble *longitude,
1085                           gdouble *latitude)
1086 {
1087 	*longitude = point->longitude;
1088 	*latitude = point->latitude;
1089 }
1090 
1091 gchar *
1092 e_map_point_get_name (EMapPoint *point)
1093 {
1094 	return point->name;
1095 }
1096 
1097 guint32
1098 e_map_point_get_color_rgba (EMapPoint *point)
1099 {
1100 	return point->rgba;
1101 }
1102 
1103 void
1104 e_map_point_set_color_rgba (EMap *map,
1105                             EMapPoint *point,
1106                             guint32 color_rgba)
1107 {
1108 	point->rgba = color_rgba;
1109 
1110 	if (!((EMapPrivate *) map->priv)->frozen)
1111 	{
1112 		/* TODO: Redraw area around point only */
1113 
1114 		update_render_point (map, point);
1115 		repaint_point (map, point);
1116 	}
1117 }
1118 
1119 void
1120 e_map_point_set_data (EMapPoint *point,
1121                       gpointer data)
1122 {
1123 	point->user_data = data;
1124 }
1125 
1126 gpointer
1127 e_map_point_get_data (EMapPoint *point)
1128 {
1129 	return point->user_data;
1130 }
1131 
1132 gboolean
1133 e_map_point_is_in_view (EMap *map,
1134                         EMapPoint *point)
1135 {
1136 	GtkAllocation allocation;
1137 	gdouble x, y;
1138 
1139 	if (!map->priv->map_render_surface) return FALSE;
1140 
1141 	e_map_world_to_window (map, point->longitude, point->latitude, &x, &y);
1142 	gtk_widget_get_allocation (GTK_WIDGET (map), &allocation);
1143 
1144 	if (x >= 0 && x < allocation.width &&
1145 	    y >= 0 && y < allocation.height)
1146 		return TRUE;
1147 
1148 	return FALSE;
1149 }
1150 
1151 EMapPoint *
1152 e_map_get_closest_point (EMap *map,
1153                          gdouble longitude,
1154                          gdouble latitude,
1155                          gboolean in_view)
1156 {
1157 	EMapPoint *point_chosen = NULL, *point;
1158 	gdouble min_dist = 0.0, dist;
1159 	gdouble dx, dy;
1160 	gint i;
1161 
1162 	for (i = 0; i < map->priv->points->len; i++)
1163 	{
1164 		point = g_ptr_array_index (map->priv->points, i);
1165 		if (in_view && !e_map_point_is_in_view (map, point)) continue;
1166 
1167 		dx = point->longitude - longitude;
1168 		dy = point->latitude - latitude;
1169 		dist = dx * dx + dy * dy;
1170 
1171 		if (!point_chosen || dist < min_dist)
1172 		{
1173 			min_dist = dist;
1174 			point_chosen = point;
1175 		}
1176 	}
1177 
1178 	return point_chosen;
1179 }
1180 
1181 /* ------------------ *
1182  * Internal functions *
1183  * ------------------ */
1184 
1185 static void
1186 update_and_paint (EMap *map)
1187 {
1188 	update_render_surface (map, TRUE);
1189 	gtk_widget_queue_draw (GTK_WIDGET (map));
1190 }
1191 
1192 static gint
1193 load_map_background (EMap *map,
1194                      gchar *name)
1195 {
1196 	GdkPixbuf *pb0;
1197 
1198 	pb0 = gdk_pixbuf_new_from_file (name, NULL);
1199 	if (!pb0)
1200 		return FALSE;
1201 
1202 	if (map->priv->map_pixbuf) g_object_unref (map->priv->map_pixbuf);
1203 	map->priv->map_pixbuf = pb0;
1204 	update_render_surface (map, TRUE);
1205 
1206 	return TRUE;
1207 }
1208 
1209 static void
1210 update_render_surface (EMap *map,
1211                        gboolean render_overlays)
1212 {
1213 	EMapPoint *point;
1214 	GtkAllocation allocation;
1215 	gint width, height, orig_width, orig_height;
1216 	gdouble zoom;
1217 	gint i;
1218 
1219 	if (!gtk_widget_get_realized (GTK_WIDGET (map)))
1220 		return;
1221 
1222 	gtk_widget_get_allocation (GTK_WIDGET (map), &allocation);
1223 
1224 	/* Set up value shortcuts */
1225 
1226 	width = allocation.width;
1227 	height = allocation.height;
1228 	orig_width = gdk_pixbuf_get_width (map->priv->map_pixbuf);
1229 	orig_height = gdk_pixbuf_get_height (map->priv->map_pixbuf);
1230 
1231 	/* Compute scaled width and height based on the extreme dimension */
1232 
1233 	if ((gdouble) width / orig_width > (gdouble) height / orig_height)
1234 		zoom = (gdouble) width / (gdouble) orig_width;
1235 	else
1236 		zoom = (gdouble) height / (gdouble) orig_height;
1237 
1238 	if (map->priv->zoom_state == E_MAP_ZOOMED_IN)
1239 		zoom *= 2.0;
1240 	height = (orig_height * zoom) + 0.5;
1241 	width = (orig_width * zoom) + 0.5;
1242 
1243 	/* Reallocate the pixbuf */
1244 
1245 	if (map->priv->map_render_surface)
1246 		cairo_surface_destroy (map->priv->map_render_surface);
1247 	map->priv->map_render_surface = gdk_window_create_similar_surface (
1248 		gtk_widget_get_window (GTK_WIDGET (map)),
1249 		CAIRO_CONTENT_COLOR, width, height);
1250 
1251 	/* Scale the original map into the rendering pixbuf */
1252 
1253 	if (width > 1 && height > 1) {
1254 		cairo_t *cr = cairo_create (map->priv->map_render_surface);
1255 		cairo_scale (
1256 			cr,
1257 			(gdouble) width / orig_width,
1258 			(gdouble) height / orig_height);
1259 		gdk_cairo_set_source_pixbuf (cr, map->priv->map_pixbuf, 0, 0);
1260 		cairo_paint (cr);
1261 		cairo_destroy (cr);
1262 	}
1263 
1264 	/* Compute image offsets with respect to window */
1265 
1266 	set_scroll_area (map, width, height);
1267 
1268 	if (render_overlays) {
1269 		/* Add points */
1270 
1271 		for (i = 0; i < map->priv->points->len; i++) {
1272 			point = g_ptr_array_index (map->priv->points, i);
1273 			update_render_point (map, point);
1274 		}
1275 	}
1276 }
1277 
1278 /* Redraw point in client surface */
1279 
1280 static void
1281 update_render_point (EMap *map,
1282                      EMapPoint *point)
1283 {
1284 	cairo_t *cr;
1285 	gdouble px, py;
1286 	static guchar mask1[] = { 0x00, 0x00, 0xff, 0x00, 0x00,  0x00, 0x00, 0x00,
1287 				  0x00, 0xff, 0x00, 0xff, 0x00,  0x00, 0x00, 0x00,
1288 				  0xff, 0x00, 0x00, 0x00, 0xff,  0x00, 0x00, 0x00,
1289 				  0x00, 0xff, 0x00, 0xff, 0x00,  0x00, 0x00, 0x00,
1290 				  0x00, 0x00, 0xff, 0x00, 0x00,  0x00, 0x00, 0x00 };
1291 	static guchar mask2[] = { 0x00, 0xff, 0x00,  0x00,
1292 				  0xff, 0xff, 0xff,  0x00,
1293 				  0x00, 0xff, 0x00,  0x00 };
1294 	cairo_surface_t *mask;
1295 
1296 	if (map->priv->map_render_surface == NULL)
1297 		return;
1298 
1299 	cr = cairo_create (map->priv->map_render_surface);
1300 	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
1301 
1302 	e_map_world_to_window (map, point->longitude, point->latitude, &px, &py);
1303 	px = floor (px + map->priv->xofs);
1304 	py = floor (py + map->priv->yofs);
1305 
1306 	cairo_set_source_rgb (cr, 0, 0, 0);
1307 	mask = cairo_image_surface_create_for_data (mask1, CAIRO_FORMAT_A8, 5, 5, 8);
1308 	cairo_mask_surface (cr, mask, px - 2, py - 2);
1309 	cairo_surface_destroy (mask);
1310 
1311 	cairo_set_source_rgba (
1312 		cr,
1313 		((point->rgba >> 24) & 0xff) / 255.0,
1314 		((point->rgba >> 16) & 0xff) / 255.0,
1315 		((point->rgba >>  8) & 0xff) / 255.0,
1316 		( point->rgba	& 0xff) / 255.0);
1317 	mask = cairo_image_surface_create_for_data (mask2, CAIRO_FORMAT_A8, 3, 3, 4);
1318 	cairo_mask_surface (cr, mask, px - 1, py - 1);
1319 	cairo_surface_destroy (mask);
1320 
1321 	cairo_destroy (cr);
1322 }
1323 
1324 /* Repaint point on X server */
1325 
1326 static void
1327 repaint_point (EMap *map,
1328                EMapPoint *point)
1329 {
1330 	gdouble px, py;
1331 
1332 	if (!gtk_widget_is_drawable (GTK_WIDGET (map)))
1333 		return;
1334 
1335 	e_map_world_to_window (map, point->longitude, point->latitude, &px, &py);
1336 
1337 	gtk_widget_queue_draw_area (
1338 		GTK_WIDGET (map),
1339 		(gint) px - 2, (gint) py - 2,
1340 		5, 5);
1341 }
1342 
1343 static void
1344 center_at (EMap *map,
1345            gdouble longitude,
1346            gdouble latitude)
1347 {
1348 	GtkAllocation allocation;
1349 	gint pb_width, pb_height;
1350 	gdouble x, y;
1351 
1352 	e_map_world_to_render_surface (map, longitude, latitude, &x, &y);
1353 
1354 	pb_width = E_MAP_GET_WIDTH (map);
1355 	pb_height = E_MAP_GET_HEIGHT (map);
1356 
1357 	gtk_widget_get_allocation (GTK_WIDGET (map), &allocation);
1358 
1359 	x = CLAMP (x - (allocation.width / 2), 0, pb_width - allocation.width);
1360 	y = CLAMP (y - (allocation.height / 2), 0, pb_height - allocation.height);
1361 
1362 	gtk_adjustment_set_value (map->priv->hadjustment, x);
1363 	gtk_adjustment_set_value (map->priv->vadjustment, y);
1364 
1365 	gtk_widget_queue_draw (GTK_WIDGET (map));
1366 }
1367 
1368 /* Scrolls the view to the specified offsets.  Does not perform range checking!  */
1369 
1370 static void
1371 scroll_to (EMap *map,
1372            gint x,
1373            gint y)
1374 {
1375 	gint xofs, yofs;
1376 
1377 	/* Compute offsets and check bounds */
1378 
1379 	xofs = x - map->priv->xofs;
1380 	yofs = y - map->priv->yofs;
1381 
1382 	if (xofs == 0 && yofs == 0)
1383 		return;
1384 
1385 	map->priv->xofs = x;
1386 	map->priv->yofs = y;
1387 
1388 	gtk_widget_queue_draw (GTK_WIDGET (map));
1389 }
1390 
1391 static void
1392 set_scroll_area (EMap *view,
1393                  gint width,
1394                  gint height)
1395 {
1396 	EMapPrivate *priv;
1397 	GtkAllocation allocation;
1398 
1399 	priv = view->priv;
1400 
1401 	if (!gtk_widget_get_realized (GTK_WIDGET (view)))
1402 		return;
1403 
1404 	if (!priv->hadjustment || !priv->vadjustment)
1405 		return;
1406 
1407 	g_object_freeze_notify (G_OBJECT (priv->hadjustment));
1408 	g_object_freeze_notify (G_OBJECT (priv->vadjustment));
1409 
1410 	gtk_widget_get_allocation (GTK_WIDGET (view), &allocation);
1411 
1412 	priv->xofs = CLAMP (priv->xofs, 0, width - allocation.width);
1413 	priv->yofs = CLAMP (priv->yofs, 0, height - allocation.height);
1414 
1415 	gtk_adjustment_configure (
1416 		priv->hadjustment,
1417 		priv->xofs,
1418 		0, width,
1419 		SCROLL_STEP_SIZE,
1420 		allocation.width / 2,
1421 		allocation.width);
1422 	gtk_adjustment_configure (
1423 		priv->vadjustment,
1424 		priv->yofs,
1425 		0, height,
1426 		SCROLL_STEP_SIZE,
1427 		allocation.height / 2,
1428 		allocation.height);
1429 
1430 	g_object_thaw_notify (G_OBJECT (priv->hadjustment));
1431 	g_object_thaw_notify (G_OBJECT (priv->vadjustment));
1432 }