Contents of /trunk/src/area_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 224 - (show annotations)
Tue Jul 14 11:15:40 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 27288 byte(s)
More area-edit/project setup adjustments
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSM2Go is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "appdata.h"
21
22 #ifdef ENABLE_OSM_GPS_MAP
23 #include "osm-gps-map.h"
24 #endif
25
26 #define TAB_LABEL_MAP "Map"
27 #define TAB_LABEL_DIRECT "Direct"
28 #define TAB_LABEL_EXTENT "Extent"
29 #define TAB_LABEL_MM "Maemo Mapper"
30
31 /* limit of square kilometers above the warning is enabled */
32 #define WARN_OVER 5.0
33
34 typedef struct {
35 GtkWidget *dialog, *notebook;
36 area_edit_t *area;
37 pos_t min, max; /* local copy to work on */
38 GtkWidget *minlat, *maxlat, *minlon, *maxlon;
39 GtkWidget *warning;
40
41 struct {
42 GtkWidget *minlat, *maxlat, *minlon, *maxlon;
43 } direct;
44
45 struct {
46 GtkWidget *lat, *lon, *height, *width, *mil_km;
47 gboolean is_mil;
48 } extent;
49
50 #ifdef USE_HILDON
51 struct {
52 GtkWidget *fetch;
53 } mmapper;
54 #endif
55
56 #ifdef ENABLE_OSM_GPS_MAP
57 struct {
58 GtkWidget *widget;
59 GtkWidget *zoomin, *zoomout, *center, *modesel, *gps;
60 gboolean needs_redraw, drag_mode;
61 gint handler_id;
62 coord_t start;
63 } map;
64 #endif
65 } context_t;
66
67 static void parse_and_set_lat(GtkWidget *src, pos_float_t *store) {
68 pos_float_t i = pos_parse_lat((char*)gtk_entry_get_text(GTK_ENTRY(src)));
69 if(pos_lat_valid(i))
70 *store = i;
71 }
72
73 static void parse_and_set_lon(GtkWidget *src, pos_float_t *store) {
74 pos_float_t i = pos_parse_lon((char*)gtk_entry_get_text(GTK_ENTRY(src)));
75 if(pos_lon_valid(i))
76 *store = i;
77 }
78
79 static gboolean current_tab_is(context_t *context, gint page_num, char *str) {
80 if(page_num < 0)
81 page_num =
82 gtk_notebook_get_current_page(GTK_NOTEBOOK(context->notebook));
83
84 if(page_num < 0) return FALSE;
85
86 GtkWidget *w =
87 gtk_notebook_get_nth_page(GTK_NOTEBOOK(context->notebook), page_num);
88 const char *name =
89 gtk_notebook_get_tab_label_text(GTK_NOTEBOOK(context->notebook), w);
90
91 return(strcasecmp(name, _(str)) == 0);
92 }
93
94 static void on_area_warning_clicked(GtkButton *button, gpointer data) {
95 context_t *context = (context_t*)data;
96
97 /* compute area size */
98 pos_float_t center_lat = (context->max.lat + context->min.lat)/2;
99 double vscale = DEG2RAD(POS_EQ_RADIUS / 1000.0);
100 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS / 1000.0);
101
102 double area = vscale * (context->max.lat - context->min.lat) *
103 hscale * (context->max.lon - context->min.lon);
104
105 warningf(context->dialog,
106 _("The currently selected area is %.02f km² (%.02f mi²) in size. "
107 "This is more than the recommended %.02f km² (%.02f mi²).\n\n"
108 "Continuing may result in a big download and low mapping performance "
109 "in a densly mapped area (e.g. cities)!"),
110 area, area/(KMPMIL*KMPMIL),
111 WARN_OVER, WARN_OVER/(KMPMIL*KMPMIL)
112 );
113
114 }
115
116 static void area_main_update(context_t *context) {
117 pos_lat_label_set(context->minlat, context->min.lat);
118 pos_lat_label_set(context->maxlat, context->max.lat);
119 pos_lon_label_set(context->minlon, context->min.lon);
120 pos_lon_label_set(context->maxlon, context->max.lon);
121
122 /* check if area size exceeds recommended values */
123 pos_float_t center_lat = (context->max.lat + context->min.lat)/2;
124 double vscale = DEG2RAD(POS_EQ_RADIUS / 1000.0);
125 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS / 1000.0);
126
127 double area = vscale * (context->max.lat - context->min.lat) *
128 hscale * (context->max.lon - context->min.lon);
129
130 if(area > WARN_OVER)
131 gtk_widget_show(context->warning);
132 else
133 gtk_widget_hide(context->warning);
134 }
135
136 #ifdef ENABLE_OSM_GPS_MAP
137 #define LOG2(x) (log(x) / log(2))
138
139 static GSList *pos_append_rad(GSList *list, pos_float_t lat, pos_float_t lon) {
140 coord_t *coo = g_new0(coord_t, 1);
141 coo->rlat = lat;
142 coo->rlon = lon;
143 list = g_slist_append(list, coo);
144 return list;
145 }
146
147 static GSList *pos_append(GSList *list, pos_float_t lat, pos_float_t lon) {
148 return pos_append_rad(list, DEG2RAD(lat), DEG2RAD(lon));
149 }
150
151 /* the contents of the map tab have been changed */
152 static void map_update(context_t *context, gboolean forced) {
153
154 /* map is first tab (page 0) */
155 if(!forced && !current_tab_is(context, -1, TAB_LABEL_MAP)) {
156 context->map.needs_redraw = TRUE;
157 return;
158 }
159
160 /* check if the position is invalid */
161 if(isnan(context->min.lat) || isnan(context->min.lon) ||
162 isnan(context->min.lat) || isnan(context->min.lon)) {
163
164 /* no coordinates given: display the entire world */
165 osm_gps_map_set_mapcenter(OSM_GPS_MAP(context->map.widget),
166 0.0, 0.0, 1);
167
168 osm_gps_map_clear_tracks(OSM_GPS_MAP(context->map.widget));
169 } else {
170
171 pos_float_t center_lat = (context->max.lat + context->min.lat)/2;
172 pos_float_t center_lon = (context->max.lon + context->min.lon)/2;
173
174 /* we know the widgets pixel size, we know the required real size, */
175 /* we want the zoom! */
176 double vzoom = LOG2((45.0 * context->map.widget->allocation.height)/
177 ((context->max.lat - context->min.lat)*32.0));
178
179 double hzoom = LOG2((45.0 * context->map.widget->allocation.width)/
180 ((context->max.lon - context->min.lon)*32.0));
181
182 osm_gps_map_set_center(OSM_GPS_MAP(context->map.widget),
183 center_lat, center_lon);
184
185 /* use smallest zoom, so everything fits on screen */
186 osm_gps_map_set_zoom(OSM_GPS_MAP(context->map.widget),
187 (vzoom < hzoom)?vzoom:hzoom);
188
189 /* ---------- draw border (as a gps track) -------------- */
190 osm_gps_map_clear_tracks(OSM_GPS_MAP(context->map.widget));
191
192 GSList *box = pos_append(NULL, context->min.lat, context->min.lon);
193 box = pos_append(box, context->max.lat, context->min.lon);
194 box = pos_append(box, context->max.lat, context->max.lon);
195 box = pos_append(box, context->min.lat, context->max.lon);
196 box = pos_append(box, context->min.lat, context->min.lon);
197
198 osm_gps_map_add_track(OSM_GPS_MAP(context->map.widget), box);
199 }
200
201 context->map.needs_redraw = FALSE;
202 }
203
204 static gboolean on_map_configure(GtkWidget *widget,
205 GdkEventConfigure *event,
206 context_t *context) {
207 map_update(context, FALSE);
208 return FALSE;
209 }
210 #endif
211
212 /* the contents of the direct tab have been changed */
213 static void direct_update(context_t *context) {
214 pos_lat_entry_set(context->direct.minlat, context->min.lat);
215 pos_lon_entry_set(context->direct.minlon, context->min.lon);
216 pos_lat_entry_set(context->direct.maxlat, context->max.lat);
217 pos_lon_entry_set(context->direct.maxlon, context->max.lon);
218 }
219
220 /* update the contents of the extent tab */
221 static void extent_update(context_t *context) {
222 pos_float_t center_lat = (context->max.lat + context->min.lat)/2;
223 pos_float_t center_lon = (context->max.lon + context->min.lon)/2;
224
225 pos_lat_entry_set(context->extent.lat, center_lat);
226 pos_lat_entry_set(context->extent.lon, center_lon);
227
228 double vscale = DEG2RAD(POS_EQ_RADIUS / 1000.0);
229 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS / 1000.0);
230
231 double height = vscale * (context->max.lat - context->min.lat);
232 double width = hscale * (context->max.lon - context->min.lon);
233
234 pos_dist_entry_set(context->extent.width, width, context->extent.is_mil);
235 pos_dist_entry_set(context->extent.height, height, context->extent.is_mil);
236 }
237
238 static void callback_modified_direct(GtkWidget *widget, gpointer data) {
239 context_t *context = (context_t*)data;
240
241 /* direct is second tab (page 1) */
242 if(!current_tab_is(context, -1, TAB_LABEL_DIRECT))
243 return;
244
245 /* parse the fields from the direct entry pad */
246 parse_and_set_lat(context->direct.minlat, &context->min.lat);
247 parse_and_set_lon(context->direct.minlon, &context->min.lon);
248 parse_and_set_lat(context->direct.maxlat, &context->max.lat);
249 parse_and_set_lon(context->direct.maxlon, &context->max.lon);
250
251 area_main_update(context);
252
253 /* also adjust other views */
254 extent_update(context);
255 #ifdef ENABLE_OSM_GPS_MAP
256 map_update(context, FALSE);
257 #endif
258 }
259
260 static void callback_modified_extent(GtkWidget *widget, gpointer data) {
261 context_t *context = (context_t*)data;
262
263 /* extent is third tab (page 2) */
264 if(!current_tab_is(context, -1, TAB_LABEL_EXTENT))
265 return;
266
267 pos_float_t center_lat = pos_lat_get(context->extent.lat);
268 pos_float_t center_lon = pos_lon_get(context->extent.lon);
269
270 if(!pos_lat_valid(center_lat) || !pos_lon_valid(center_lon))
271 return;
272
273 double vscale = DEG2RAD(POS_EQ_RADIUS / 1000.0);
274 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS / 1000.0);
275
276 double height = pos_dist_get(context->extent.height, context->extent.is_mil);
277 double width = pos_dist_get(context->extent.width, context->extent.is_mil);
278
279 height /= 2 * vscale;
280 context->min.lat = center_lat - height;
281 context->max.lat = center_lat + height;
282
283 width /= 2 * hscale;
284 context->min.lon = center_lon - width;
285 context->max.lon = center_lon + width;
286
287 area_main_update(context);
288
289 /* also update other tabs */
290 direct_update(context);
291 #ifdef ENABLE_OSM_GPS_MAP
292 map_update(context, FALSE);
293 #endif
294 }
295
296 static void callback_modified_unit(GtkWidget *widget, gpointer data) {
297 context_t *context = (context_t*)data;
298
299 /* get current values */
300 double height = pos_dist_get(context->extent.height, context->extent.is_mil);
301 double width = pos_dist_get(context->extent.width, context->extent.is_mil);
302
303 /* adjust unit flag */
304 context->extent.is_mil = gtk_combo_box_get_active(
305 GTK_COMBO_BOX(context->extent.mil_km)) == 0;
306
307 /* save values */
308 pos_dist_entry_set(context->extent.width, width, context->extent.is_mil);
309 pos_dist_entry_set(context->extent.height, height, context->extent.is_mil);
310 }
311
312 #ifdef USE_HILDON
313 static void callback_fetch_mm_clicked(GtkButton *button, gpointer data) {
314 context_t *context = (context_t*)data;
315
316 if(!dbus_mm_set_position(context->area->appdata->osso_context, NULL)) {
317 errorf(context->dialog,
318 _("Unable to communicate with Maemo Mapper. "
319 "You need to have Maemo Mapper installed "
320 "to use this feature."));
321 return;
322 }
323
324 if(!context->area->appdata->mmpos.valid) {
325 errorf(context->dialog,
326 _("No valid position received yet. You need "
327 "to scroll or zoom the Maemo Mapper view "
328 "in order to force it to send its current "
329 "view position to osm2go."));
330 return;
331 }
332
333 /* maemo mapper is fourth tab (page 3) */
334 if(!current_tab_is(context, -1, TAB_LABEL_MM))
335 return;
336
337 /* maemo mapper pos data ... */
338 pos_float_t center_lat = context->area->appdata->mmpos.pos.lat;
339 pos_float_t center_lon = context->area->appdata->mmpos.pos.lon;
340 int zoom = context->area->appdata->mmpos.zoom;
341
342 if(!pos_lat_valid(center_lat) || !pos_lon_valid(center_lon))
343 return;
344
345 double vscale = DEG2RAD(POS_EQ_RADIUS);
346 double height = 8 * (1<<zoom) / vscale;
347 context->min.lat = center_lat - height;
348 context->max.lat = center_lat + height;
349
350 double hscale = DEG2RAD(cos(DEG2RAD(center_lat)) * POS_EQ_RADIUS);
351 double width = 16 * (1<<zoom) / hscale;
352 context->min.lon = center_lon - width;
353 context->max.lon = center_lon + width;
354
355 area_main_update(context);
356
357 /* also update other tabs */
358 direct_update(context);
359 extent_update(context);
360 #ifdef ENABLE_OSM_GPS_MAP
361 map_update(context, FALSE);
362 #endif
363 }
364 #endif
365
366 #ifdef ENABLE_OSM_GPS_MAP
367
368 static gboolean
369 on_map_button_press_event(GtkWidget *widget,
370 GdkEventButton *event, context_t *context) {
371 if(!context->map.drag_mode) {
372 OsmGpsMap *map = OSM_GPS_MAP(context->map.widget);
373
374 /* remove existing marker */
375 osm_gps_map_clear_tracks(map);
376
377 /* and remember this location as the start */
378 context->map.start =
379 osm_gps_map_get_co_ordinates(map, (int)event->x, (int)event->y);
380
381 return TRUE;
382 }
383
384 return FALSE;
385 }
386
387 static gboolean
388 on_map_motion_notify_event(GtkWidget *widget,
389 GdkEventMotion *event, context_t *context) {
390 if(!context->map.drag_mode &&
391 !isnan(context->map.start.rlon) &&
392 !isnan(context->map.start.rlat)) {
393 OsmGpsMap *map = OSM_GPS_MAP(context->map.widget);
394
395 /* remove existing marker */
396 osm_gps_map_clear_tracks(map);
397
398 coord_t start = context->map.start, end =
399 osm_gps_map_get_co_ordinates(map, (int)event->x, (int)event->y);
400
401 GSList *box = pos_append_rad(NULL, start.rlat, start.rlon);
402 box = pos_append_rad(box, end.rlat, start.rlon);
403 box = pos_append_rad(box, end.rlat, end.rlon);
404 box = pos_append_rad(box, start.rlat, end.rlon);
405 box = pos_append_rad(box, start.rlat, start.rlon);
406
407 osm_gps_map_add_track(map, box);
408
409 return TRUE;
410 }
411
412 return FALSE;
413 }
414
415 static gboolean
416 on_map_button_release_event(GtkWidget *widget,
417 GdkEventButton *event, context_t *context) {
418 if(!context->map.drag_mode &&
419 !isnan(context->map.start.rlon) &&
420 !isnan(context->map.start.rlat)) {
421 OsmGpsMap *map = OSM_GPS_MAP(context->map.widget);
422
423 coord_t start = context->map.start, end =
424 osm_gps_map_get_co_ordinates(map, (int)event->x, (int)event->y);
425
426 GSList *box = pos_append_rad(NULL, start.rlat, start.rlon);
427 box = pos_append_rad(box, end.rlat, start.rlon);
428 box = pos_append_rad(box, end.rlat, end.rlon);
429 box = pos_append_rad(box, start.rlat, end.rlon);
430 box = pos_append_rad(box, start.rlat, start.rlon);
431
432 osm_gps_map_add_track(map, box);
433
434 if(start.rlat < end.rlat) {
435 context->min.lat = RAD2DEG(start.rlat);
436 context->max.lat = RAD2DEG(end.rlat);
437 } else {
438 context->min.lat = RAD2DEG(end.rlat);
439 context->max.lat = RAD2DEG(start.rlat);
440 }
441
442 if(start.rlon < end.rlon) {
443 context->min.lon = RAD2DEG(start.rlon);
444 context->max.lon = RAD2DEG(end.rlon);
445 } else {
446 context->min.lon = RAD2DEG(end.rlon);
447 context->max.lon = RAD2DEG(start.rlon);
448 }
449
450 area_main_update(context);
451 direct_update(context);
452 extent_update(context);
453
454 context->map.start.rlon = context->map.start.rlat = NAN;
455
456 return TRUE;
457 }
458
459 return FALSE;
460 }
461
462 static void map_zoom(context_t *context, int step) {
463 int zoom;
464 OsmGpsMap *map = OSM_GPS_MAP(context->map.widget);
465 g_object_get(map, "zoom", &zoom, NULL);
466 zoom = osm_gps_map_set_zoom(map, zoom+step);
467
468 /* enable/disable zoom buttons as required */
469 gtk_widget_set_sensitive(context->map.zoomin, zoom<17);
470 gtk_widget_set_sensitive(context->map.zoomout, zoom>1);
471 }
472
473 static gboolean
474 cb_map_zoomin(GtkButton *button, context_t *context) {
475 map_zoom(context, +1);
476 return FALSE;
477 }
478
479 static gboolean
480 cb_map_zoomout(GtkButton *button, context_t *context) {
481 map_zoom(context, -1);
482 return FALSE;
483 }
484
485 static gboolean
486 cb_map_center(GtkButton *button, context_t *context) {
487 map_update(context, TRUE);
488 return FALSE;
489 }
490
491 static gboolean
492 cb_map_modesel(GtkButton *button, context_t *context) {
493 /* toggle between "find" icon and "cut" icon */
494 context->map.drag_mode = !context->map.drag_mode;
495 gtk_button_set_image(GTK_BUTTON(context->map.modesel),
496 gtk_image_new_from_stock(context->map.drag_mode?
497 GTK_STOCK_FIND:GTK_STOCK_CUT, GTK_ICON_SIZE_MENU));
498
499 return FALSE;
500 }
501
502 static gboolean
503 cb_map_gps(GtkButton *button, context_t *context) {
504 pos_t pos;
505
506 /* user clicked "gps" button -> jump to position */
507 gboolean gps_on =
508 context->area->appdata->settings &&
509 context->area->appdata->settings->enable_gps;
510
511 if(gps_on && gps_get_pos(context->area->appdata, &pos, NULL)) {
512 osm_gps_map_set_center(OSM_GPS_MAP(context->map.widget),
513 DEG2RAD(pos.lat), DEG2RAD(pos.lon));
514 }
515
516 return FALSE;
517 }
518
519 static void on_page_switch(GtkNotebook *notebook, GtkNotebookPage *page,
520 guint page_num, context_t *context) {
521
522 /* updating the map while the user manually changes some coordinates */
523 /* may confuse the map. so we delay those updates until the map tab */
524 /* is becoming visible */
525 if(current_tab_is(context, page_num, TAB_LABEL_MAP) &&
526 context->map.needs_redraw)
527 map_update(context, TRUE);
528 }
529
530 static GtkWidget
531 *map_add_button(const gchar *icon, GCallback cb, gpointer data,
532 char *tooltip) {
533 GtkWidget *button = gtk_button_new();
534 gtk_button_set_image(GTK_BUTTON(button),
535 gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU));
536 g_signal_connect(button, "clicked", cb, data);
537 #ifndef USE_HILDON
538 gtk_widget_set_tooltip_text(button, tooltip);
539 #endif
540 return button;
541 }
542
543 static gboolean map_gps_update(gpointer data) {
544 context_t *context = (context_t*)data;
545
546 gboolean gps_on =
547 context->area->appdata->settings &&
548 context->area->appdata->settings->enable_gps;
549
550 gboolean gps_fix = gps_on &&
551 gps_get_pos(context->area->appdata, NULL, NULL);
552
553 gtk_widget_set_sensitive(context->map.gps, gps_fix);
554
555 return TRUE;
556 }
557
558 #endif
559
560 gboolean area_edit(area_edit_t *area) {
561 GtkWidget *vbox;
562 gboolean ok = FALSE;
563
564 context_t context;
565 memset(&context, 0, sizeof(context_t));
566 context.area = area;
567 context.min.lat = area->min->lat;
568 context.min.lon = area->min->lon;
569 context.max.lat = area->max->lat;
570 context.max.lon = area->max->lon;
571
572 context.dialog =
573 misc_dialog_new(MISC_DIALOG_HIGH, _("Area editor"),
574 GTK_WINDOW(area->parent),
575 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
576 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
577 NULL);
578
579 GtkWidget *table = gtk_table_new(5, 2, FALSE); // x, y
580
581 GtkWidget *label = gtk_label_new(_("Latitude:"));
582 misc_table_attach(table, label, 0, 0);
583 context.minlat = pos_lat_label_new(area->min->lat);
584 misc_table_attach(table, context.minlat, 1, 0);
585 label = gtk_label_new(_("to"));
586 misc_table_attach(table, label, 2, 0);
587 context.maxlat = pos_lat_label_new(area->max->lat);
588 misc_table_attach(table, context.maxlat, 3, 0);
589
590 label = gtk_label_new(_("Longitude:"));
591 misc_table_attach(table, label, 0, 1);
592 context.minlon = pos_lon_label_new(area->min->lon);
593 misc_table_attach(table, context.minlon, 1, 1);
594 label = gtk_label_new(_("to"));
595 misc_table_attach(table, label, 2, 1);
596 context.maxlon = pos_lon_label_new(area->max->lon);
597 misc_table_attach(table, context.maxlon, 3, 1);
598
599 context.warning = gtk_button_new();
600 gtk_button_set_image(GTK_BUTTON(context.warning),
601 gtk_image_new_from_stock(GTK_STOCK_DIALOG_WARNING,
602 GTK_ICON_SIZE_BUTTON));
603 g_signal_connect(context.warning, "clicked",
604 G_CALLBACK(on_area_warning_clicked), &context);
605 gtk_table_attach_defaults(GTK_TABLE(table), context.warning, 4, 5, 0, 2);
606
607 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context.dialog)->vbox),
608 table, FALSE, FALSE, 0);
609
610 context.notebook = gtk_notebook_new();
611
612 #ifdef ENABLE_OSM_GPS_MAP
613 /* ------------- fetch from map ------------------------ */
614
615 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
616
617 context.map.needs_redraw = FALSE;
618 context.map.widget = g_object_new(OSM_TYPE_GPS_MAP,
619 "repo-uri", MAP_SOURCE_OPENSTREETMAP,
620 "proxy-uri", misc_get_proxy_uri(area->settings),
621 NULL);
622
623 g_signal_connect(G_OBJECT(context.map.widget), "configure-event",
624 G_CALLBACK(on_map_configure), &context);
625 g_signal_connect(G_OBJECT(context.map.widget), "button-press-event",
626 G_CALLBACK(on_map_button_press_event), &context);
627 g_signal_connect(G_OBJECT(context.map.widget), "motion-notify-event",
628 G_CALLBACK(on_map_motion_notify_event), &context);
629 g_signal_connect(G_OBJECT(context.map.widget), "button-release-event",
630 G_CALLBACK(on_map_button_release_event), &context);
631
632 gtk_box_pack_start_defaults(GTK_BOX(hbox), context.map.widget);
633
634 /* zoom button box */
635 vbox = gtk_vbox_new(FALSE,0);
636
637 context.map.zoomin =
638 map_add_button(GTK_STOCK_ZOOM_IN, G_CALLBACK(cb_map_zoomin),
639 &context, _("Zoom in"));
640 gtk_box_pack_start(GTK_BOX(vbox), context.map.zoomin, FALSE, FALSE, 0);
641
642 context.map.zoomout =
643 map_add_button(GTK_STOCK_ZOOM_OUT, G_CALLBACK(cb_map_zoomout),
644 &context, _("Zoom out"));
645 gtk_box_pack_start(GTK_BOX(vbox), context.map.zoomout, FALSE, FALSE, 0);
646
647 context.map.center =
648 map_add_button(GTK_STOCK_HOME, G_CALLBACK(cb_map_center),
649 &context, _("Center selected area"));
650 gtk_box_pack_start(GTK_BOX(vbox), context.map.center, FALSE, FALSE, 0);
651
652 context.map.gps =
653 map_add_button(GTK_STOCK_ABOUT, G_CALLBACK(cb_map_gps),
654 &context, _("Jump to GPS position"));
655 gtk_widget_set_sensitive(context.map.gps, FALSE);
656 /* install handler for timed updates of the gps button */
657 context.map.handler_id = gtk_timeout_add(1000, map_gps_update, &context);
658 gtk_box_pack_start(GTK_BOX(vbox), context.map.gps, FALSE, FALSE, 0);
659
660 context.map.drag_mode = TRUE;
661 context.map.start.rlon = context.map.start.rlat = NAN;
662 context.map.modesel =
663 map_add_button(GTK_STOCK_CUT, G_CALLBACK(cb_map_modesel),
664 &context, _("Toggle scroll/select"));
665 gtk_box_pack_start(GTK_BOX(vbox), context.map.modesel, FALSE, FALSE, 0);
666
667 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
668
669 gtk_notebook_append_page(GTK_NOTEBOOK(context.notebook),
670 hbox, gtk_label_new(_(TAB_LABEL_MAP)));
671 #endif
672
673 /* ------------ direct min/max edit --------------- */
674
675 vbox = gtk_vbox_new(FALSE, 10);
676 table = gtk_table_new(3, 3, FALSE); // x, y
677 gtk_table_set_col_spacings(GTK_TABLE(table), 10);
678 gtk_table_set_row_spacings(GTK_TABLE(table), 5);
679
680 context.direct.minlat = pos_lat_entry_new(0.0);
681 misc_table_attach(table, context.direct.minlat, 0, 0);
682 label = gtk_label_new(_("to"));
683 misc_table_attach(table, label, 1, 0);
684 context.direct.maxlat = pos_lat_entry_new(0.0);
685 misc_table_attach(table, context.direct.maxlat, 2, 0);
686
687 context.direct.minlon = pos_lon_entry_new(area->min->lon);
688 misc_table_attach(table, context.direct.minlon, 0, 1);
689 label = gtk_label_new(_("to"));
690 misc_table_attach(table, label, 1, 1);
691 context.direct.maxlon = pos_lon_entry_new(0.0);
692 misc_table_attach(table, context.direct.maxlon, 2, 1);
693
694 /* setup this page */
695 direct_update(&context);
696
697 g_signal_connect(G_OBJECT(context.direct.minlat), "changed",
698 G_CALLBACK(callback_modified_direct), &context);
699 g_signal_connect(G_OBJECT(context.direct.minlon), "changed",
700 G_CALLBACK(callback_modified_direct), &context);
701 g_signal_connect(G_OBJECT(context.direct.maxlat), "changed",
702 G_CALLBACK(callback_modified_direct), &context);
703 g_signal_connect(G_OBJECT(context.direct.maxlon), "changed",
704 G_CALLBACK(callback_modified_direct), &context);
705
706
707 /* --- hint --- */
708 label = gtk_label_new(_("(recommended min/max diff <0.03 degrees)"));
709 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 3, 2, 3);
710
711 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
712 gtk_notebook_append_page(GTK_NOTEBOOK(context.notebook),
713 vbox, gtk_label_new(_(TAB_LABEL_DIRECT)));
714
715 /* ------------- center/extent edit ------------------------ */
716
717 vbox = gtk_vbox_new(FALSE, 10);
718 table = gtk_table_new(3, 4, FALSE); // x, y
719 gtk_table_set_col_spacings(GTK_TABLE(table), 10);
720 gtk_table_set_row_spacings(GTK_TABLE(table), 5);
721
722 label = gtk_label_new(_("Center:"));
723 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
724 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
725 context.extent.lat = pos_lat_entry_new(0.0);
726 gtk_table_attach_defaults(GTK_TABLE(table), context.extent.lat, 1, 2, 0, 1);
727 context.extent.lon = pos_lon_entry_new(0.0);
728 gtk_table_attach_defaults(GTK_TABLE(table), context.extent.lon, 2, 3, 0, 1);
729
730 gtk_table_set_row_spacing(GTK_TABLE(table), 0, 10);
731
732 label = gtk_label_new(_("Width:"));
733 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
734 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
735 context.extent.width = gtk_entry_new();
736 gtk_table_attach_defaults(GTK_TABLE(table), context.extent.width, 1, 2, 1, 2);
737
738 label = gtk_label_new(_("Height:"));
739 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
740 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
741 context.extent.height = gtk_entry_new();
742 gtk_table_attach_defaults(GTK_TABLE(table),
743 context.extent.height, 1, 2, 2, 3);
744
745 context.extent.mil_km = gtk_combo_box_new_text();
746 gtk_combo_box_append_text(GTK_COMBO_BOX(context.extent.mil_km), _("mi"));
747 gtk_combo_box_append_text(GTK_COMBO_BOX(context.extent.mil_km), _("km"));
748 gtk_combo_box_set_active(GTK_COMBO_BOX(context.extent.mil_km), 1); // km
749
750 gtk_table_attach(GTK_TABLE(table), context.extent.mil_km, 2, 3, 1, 3,
751 0, 0, 0, 0);
752
753 /* setup this page */
754 extent_update(&context);
755
756 /* connect signals after inital update to avoid confusion */
757 g_signal_connect(G_OBJECT(context.extent.lat), "changed",
758 G_CALLBACK(callback_modified_extent), &context);
759 g_signal_connect(G_OBJECT(context.extent.lon), "changed",
760 G_CALLBACK(callback_modified_extent), &context);
761 g_signal_connect(G_OBJECT(context.extent.width), "changed",
762 G_CALLBACK(callback_modified_extent), &context);
763 g_signal_connect(G_OBJECT(context.extent.height), "changed",
764 G_CALLBACK(callback_modified_extent), &context);
765 g_signal_connect(G_OBJECT(context.extent.mil_km), "changed",
766 G_CALLBACK(callback_modified_unit), &context);
767
768 /* --- hint --- */
769 label = gtk_label_new(_("(recommended width/height < 2km/1.25mi)"));
770 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 3, 3, 4);
771
772 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
773 gtk_notebook_append_page(GTK_NOTEBOOK(context.notebook),
774 vbox, gtk_label_new(_(TAB_LABEL_EXTENT)));
775
776 #ifdef USE_HILDON
777 /* ------------- fetch from maemo mapper ------------------------ */
778
779 vbox = gtk_vbox_new(FALSE, 8);
780 context.mmapper.fetch =
781 gtk_button_new_with_label(_("Get from Maemo Mapper"));
782 gtk_box_pack_start(GTK_BOX(vbox), context.mmapper.fetch, FALSE, FALSE, 0);
783
784 g_signal_connect(G_OBJECT(context.mmapper.fetch), "clicked",
785 G_CALLBACK(callback_fetch_mm_clicked), &context);
786
787 /* --- hint --- */
788 label = gtk_label_new(_("(recommended MM zoom level < 7)"));
789 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
790
791
792 gtk_notebook_append_page(GTK_NOTEBOOK(context.notebook),
793 vbox, gtk_label_new(_(TAB_LABEL_MM)));
794 #endif
795
796 /* ------------------------------------------------------ */
797
798 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context.dialog)->vbox),
799 context.notebook);
800
801 #ifdef ENABLE_OSM_GPS_MAP
802 g_signal_connect(G_OBJECT(context.notebook), "switch-page",
803 G_CALLBACK(on_page_switch), &context);
804 #endif
805
806 gtk_widget_show_all(context.dialog);
807
808 area_main_update(&context);
809
810 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context.dialog))) {
811 /* copy modified values back to given storage */
812 area->min->lat = context.min.lat;
813 area->min->lon = context.min.lon;
814 area->max->lat = context.max.lat;
815 area->max->lon = context.max.lon;
816 ok = TRUE;
817 }
818
819 #ifdef ENABLE_OSM_GPS_MAP
820 gtk_timeout_remove(context.map.handler_id);
821 #endif
822
823 gtk_widget_destroy(context.dialog);
824
825 return ok;
826 }