Contents of /trunk/src/area_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


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