Contents of /trunk/src/area_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


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