Contents of /trunk/src/misc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 218 - (show annotations)
Fri Nov 27 08:58:48 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 26552 byte(s)
More unified coordinate sources
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of GPXView.
5 *
6 * GPXView 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 * GPXView 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 GPXView. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <math.h>
21 #include <string.h>
22 #include <ctype.h>
23
24 #include <glib.h>
25 #include <glib/gstdio.h>
26
27 #include "gpxview.h"
28
29 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR >= 5)
30 #include <hildon/hildon-entry.h>
31 #include <hildon/hildon-touch-selector.h>
32 #include <hildon/hildon-picker-button.h>
33 #include <hildon/hildon-picker-dialog.h>
34 #endif
35
36 #ifdef FREMANTLE
37 #define PICKER_DIALOG
38 #endif
39
40 char strlastchr(char *str) {
41 return str[strlen(str)]-1;
42 }
43
44 /* make sure the entire path "dir" exists and create it if not */
45 int checkdir(char *dir) {
46 struct stat filestat;
47 char *p = dir, tmp;
48
49 /* don't try to create root dir */
50 if(p[0] == '/') p++;
51
52 do {
53 while(*p && *p != '/') p++;
54 tmp = *p;
55 *p = 0;
56
57 int err = stat(dir, &filestat);
58 if(err) {
59 if(mkdir(dir, S_IRWXU) != 0) {
60 perror("mkdir()");
61 *p++ = tmp;
62 return -1;
63 }
64 } else {
65 if(!filestat.st_mode & S_IFDIR) {
66 printf("File %s exists and is _no_ directory\n", dir);
67 *p++ = tmp;
68 return -1;
69 }
70 }
71
72 *p++ = tmp;
73 } while(tmp && strchr(p, '/'));
74
75 return 0;
76 }
77
78 void pos_lat_str(char *str, int len, float latitude) {
79 char *c = _("N");
80 float integral, fractional;
81
82 if(isnan(latitude))
83 str[0] = 0;
84 else {
85 if(latitude < 0) { latitude = fabs(latitude); c = _("S"); }
86 fractional = modff(latitude, &integral);
87
88 snprintf(str, len, "%s %02d° %06.3f'", c, (int)integral, fractional*60.0);
89 }
90 }
91
92 GtkWidget *pos_lat(float latitude, int size, int strikethrough) {
93 char str[32];
94
95 pos_lat_str(str, sizeof(str), latitude);
96 return gtk_label_attrib(str, size, strikethrough);
97 }
98
99 void pos_lon_str(char *str, int len, float longitude) {
100 char *c = _("E");
101 float integral, fractional;
102
103 if(isnan(longitude))
104 str[0] = 0;
105 else {
106 if(longitude < 0) { longitude = fabs(longitude); c = _("W"); }
107 fractional = modff(longitude, &integral);
108
109 snprintf(str, len, "%s %03d° %06.3f'", c, (int)integral, fractional*60.0);
110 }
111 }
112
113 GtkWidget *pos_lon(float longitude, int size, int strikethrough) {
114 char str[32];
115
116 pos_lon_str(str, sizeof(str), longitude);
117 return gtk_label_attrib(str, size, strikethrough);
118 }
119
120 float pos_parse_lat(char *str) {
121 int integral_int;
122 float fractional;
123 char c;
124
125 if(sscanf(str, "%c %d° %f'", &c, &integral_int, &fractional) == 3) {
126 c = toupper(c);
127
128 if(c != 'S' && c != 'N')
129 return NAN;
130
131 /* prevent -0.0 */
132 if(!integral_int && (fractional == 0.0))
133 return 0.0;
134
135 return ((c == 'S')?-1:+1) * (integral_int + fractional/60.0);
136 }
137
138 return NAN;
139 }
140
141 float pos_parse_lon(char *str) {
142 int integral_int;
143 float fractional;
144 char c;
145
146 if(sscanf(str, "%c %d° %f'", &c, &integral_int, &fractional) == 3) {
147 c = toupper(c);
148
149 /* O is german "Ost" for "East" */
150 if(c != 'E' && c != 'W' && c != 'O')
151 return NAN;
152
153 /* prevent -0.0 */
154 if(!integral_int && (fractional == 0.0))
155 return 0.0;
156
157 return ((c == 'W')?-1:+1) * (integral_int + fractional/60.0);
158 }
159
160 return NAN;
161 }
162
163 const char *pos_get_bearing_str(pos_t from, pos_t to) {
164 static const char *bear_str[]={
165 "N", "NE", "E", "SE", "S", "SW", "W", "NW", "" };
166
167 float bearing = gpx_pos_get_bearing(from, to);
168 if(!isnan(bearing)) {
169 int idx = (bearing+22.5)/45.0;
170 /* make sure we stay in icon bounds */
171 while(idx < 0) idx += 8;
172 while(idx > 7) idx -= 8;
173 return _(bear_str[idx]);
174 }
175
176 return bear_str[8]; // empty string
177 }
178
179 /* the maemo font size is quite huge, so we adjust some fonts */
180 /* differently on maemo and non-maemo. Basically "BIG" does nothing */
181 /* on maemo and "SMALL" only does something on maemo */
182 #ifdef USE_MAEMO
183 #define MARKUP_SMALL "<span size='small'>%s</span>"
184 GtkWidget *gtk_label_small(char *str) {
185 GtkWidget *label = gtk_label_new("");
186 char *markup = g_markup_printf_escaped(MARKUP_SMALL, str);
187 gtk_label_set_markup(GTK_LABEL(label), markup);
188 g_free(markup);
189 return label;
190 }
191 #else
192 #define MARKUP_BIG "<span size='x-large'>%s</span>"
193 GtkWidget *gtk_label_big(char *str) {
194 GtkWidget *label = gtk_label_new("");
195 char *markup = g_markup_printf_escaped(MARKUP_BIG, str);
196 gtk_label_set_markup(GTK_LABEL(label), markup);
197 g_free(markup);
198 return label;
199 }
200 #endif
201
202 void gtk_label_attrib_set(GtkWidget *label,
203 char *str, int size, int strikethrough) {
204 char format[80];
205
206 snprintf(format, sizeof(format), "<span%s%s%s>%%s</span>",
207 #ifdef USE_MAEMO
208 (size==SIZE_SMALL)?" size='small'":"",
209 #else
210 (size==SIZE_BIG)?" size='x-large'":"",
211 #endif
212 strikethrough?" strikethrough='yes'":"",
213 (strikethrough==STRIKETHROUGH_RED)?" strikethrough_color='red'":"");
214
215 char *markup = g_markup_printf_escaped(format, str);
216 // printf("markup = %s\n", markup);
217 gtk_label_set_markup(GTK_LABEL(label), markup);
218 g_free(markup);
219 }
220
221 GtkWidget *gtk_label_attrib(char *str, int size, int strikethrough) {
222 GtkWidget *label = gtk_label_new("");
223 gtk_label_attrib_set(label, str, size, strikethrough);
224 return label;
225 }
226
227 GtkWidget *gtk_button_attrib(char *str, int size, int strikethrough) {
228 GtkWidget *button = gtk_button_new_with_label("");
229 gtk_label_attrib_set(gtk_bin_get_child(GTK_BIN(button)),
230 str, size, strikethrough);
231 return button;
232 }
233
234 void textbox_disable(GtkWidget *widget) {
235 gtk_editable_set_editable(GTK_EDITABLE(widget), FALSE);
236 gtk_widget_set_sensitive(widget, FALSE);
237 }
238
239 void textbox_enable(GtkWidget *widget) {
240 gtk_widget_set_sensitive(widget, TRUE);
241 gtk_editable_set_editable(GTK_EDITABLE(widget), TRUE);
242 }
243
244 pos_t *get_pos(appdata_t *appdata) {
245 pos_t *pos = &appdata->home;
246
247 if(appdata->active_location) {
248 int i = appdata->active_location-1;
249 location_t *loc = appdata->location;
250 while(i--) loc = loc->next;
251 pos = &loc->pos;
252 }
253
254 if(appdata->use_gps) {
255 pos = gps_get_pos(appdata);
256
257 if(!pos) pos = &appdata->gps; /* use saved position */
258 else appdata->gps = *pos; /* save position */
259 }
260 return pos;
261 }
262
263 void distance_str(char *str, int len, float dist, gboolean imperial) {
264 if(isnan(dist))
265 snprintf(str, len, "---");
266 else if(imperial) {
267 /* 1 mil = 1760 yd = 5280 ft ... */
268 if(dist<0.018) snprintf(str, len, "%.1f ft", dist*5280.0);
269 else if(dist<0.055) snprintf(str, len, "%.1f yd", dist*1760.0);
270 else if(dist<0.55) snprintf(str, len, "%.0f yd", dist*1760.0);
271 else if(dist<10.0) snprintf(str, len, "%.2f mi", dist);
272 else if(dist<100.0) snprintf(str, len, "%.1f mi", dist);
273 else snprintf(str, len, "%.0f mi", dist);
274 } else {
275 if(dist<0.01) snprintf(str, len, "%.2f m", dist*1000.0);
276 else if(dist<0.1) snprintf(str, len, "%.1f m", dist*1000.0);
277 else if(dist<1.0) snprintf(str, len, "%.0f m", dist*1000.0);
278 else if(dist<100.0) snprintf(str, len, "%.1f km", dist);
279 else snprintf(str, len, "%.0f km", dist);
280 }
281 }
282
283 /* return distance in miles or kilometers */
284 float distance_parse(char *str, gboolean imperial) {
285 char unit[4];
286 float val = NAN;
287
288 if(sscanf(str, "%f %3s", &val, unit) == 2) {
289 gboolean fimp = FALSE;
290
291 if(strcasecmp(unit, "ft") == 0) { fimp = TRUE; val /= 5280.0; }
292 else if(strcasecmp(unit, "yd") == 0) { fimp = TRUE; val /= 1760.0; }
293 else if(strcasecmp(unit, "mi") == 0) { fimp = TRUE; }
294 else if(strcasecmp(unit, "m") == 0) { fimp = FALSE; val /= 1000.0; }
295 else if(strcasecmp(unit, "km") == 0) { fimp = FALSE; }
296 else val = NAN;
297
298 /* found imperial and metric requested? convert miles into kilometers */
299 if(fimp & !imperial) val *= 1.609344;
300
301 /* found metric and imperial requested? convert kilometers into miles */
302 if(!fimp & imperial) val /= 1.609344;
303 }
304 return val;
305 }
306
307 static gboolean mark(GtkWidget *widget, gboolean valid) {
308 gtk_widget_set_state(widget, valid?GTK_STATE_NORMAL:TAG_STATE);
309 return valid;
310 }
311
312 static void callback_modified_lat(GtkWidget *widget, gpointer data ) {
313 float i = pos_parse_lat((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
314 mark(widget, !isnan(i));
315 }
316
317 /* a entry that is colored red when being "active" */
318 GtkWidget *lat_entry_new(float lat) {
319 GdkColor color;
320
321 GtkWidget *widget = entry_new();
322 gdk_color_parse("#ff0000", &color);
323 gtk_widget_modify_text(widget, TAG_STATE, &color);
324
325 char str[32];
326 pos_lat_str(str, sizeof(str), lat);
327 gtk_entry_set_text(GTK_ENTRY(widget), str);
328
329 g_signal_connect(G_OBJECT(widget), "changed",
330 G_CALLBACK(callback_modified_lat), NULL);
331
332 return widget;
333 }
334
335 static void callback_modified_lon(GtkWidget *widget, gpointer data ) {
336 float i = pos_parse_lon((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
337 mark(widget, !isnan(i));
338 }
339
340 /* a entry that is colored red when filled with invalid coordinate */
341 GtkWidget *lon_entry_new(float lon) {
342 GdkColor color;
343
344 GtkWidget *widget = entry_new();
345 // gtk_entry_set_width_chars(GTK_ENTRY(widget), 14);
346
347 gdk_color_parse("#ff0000", &color);
348 gtk_widget_modify_text(widget, TAG_STATE, &color);
349
350 char str[32];
351 pos_lon_str(str, sizeof(str), lon);
352 gtk_entry_set_text(GTK_ENTRY(widget), str);
353
354 g_signal_connect(G_OBJECT(widget), "changed",
355 G_CALLBACK(callback_modified_lon), NULL);
356
357 return widget;
358 }
359
360
361 float lat_get(GtkWidget *widget) {
362 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
363 return pos_parse_lat(p);
364 }
365
366 float lon_get(GtkWidget *widget) {
367 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
368 return pos_parse_lon(p);
369 }
370
371 static void callback_modified_dist(GtkWidget *widget, gpointer data ) {
372 /* don't care for metric/imperial since we only want to know if this */
373 /* is parseable at all */
374 float i = distance_parse((char*)gtk_entry_get_text(GTK_ENTRY(widget)), FALSE);
375 mark(widget, !isnan(i));
376 }
377
378 /* a entry that is colored red when filled with invalid distance */
379 GtkWidget *dist_entry_new(float dist, gboolean mil) {
380 GdkColor color;
381 GtkWidget *widget = entry_new();
382 gdk_color_parse("#ff0000", &color);
383 gtk_widget_modify_text(widget, TAG_STATE, &color);
384
385 char str[32];
386 distance_str(str, sizeof(str), dist, mil);
387 gtk_entry_set_text(GTK_ENTRY(widget), str);
388
389 g_signal_connect(G_OBJECT(widget), "changed",
390 G_CALLBACK(callback_modified_dist), NULL);
391
392 return widget;
393 }
394
395 float dist_get(GtkWidget *widget, gboolean mil) {
396 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
397 return distance_parse(p, mil);
398 }
399
400 #ifndef USE_MAEMO
401 #ifdef ENABLE_BROWSER_INTERFACE
402 #include <libgnome/gnome-url.h>
403
404 int browser_url(appdata_t *appdata, char *url) {
405 /* taken from gnome-open, part of libgnome */
406 GError *err = NULL;
407 gnome_url_show(url, &err);
408 return 0;
409 }
410 #endif
411 #endif
412
413 /* recursively remove an entire file system */
414 void rmdir_recursive(char *path) {
415 GDir *dir = g_dir_open(path, 0, NULL);
416 if(dir) {
417 const char *name = g_dir_read_name(dir);
418 while(name) {
419 char *fullname = g_strdup_printf("%s/%s", path, name);
420 // printf("deleting %s\n", fullname);
421
422 if(g_file_test(fullname, G_FILE_TEST_IS_DIR))
423 rmdir_recursive(fullname);
424 else if(g_file_test(fullname, G_FILE_TEST_IS_REGULAR))
425 g_remove(fullname);
426
427 g_free(fullname);
428 name = g_dir_read_name(dir);
429 }
430
431 g_dir_close(dir);
432 }
433 g_rmdir(path);
434 }
435
436 #ifdef ENABLE_BROWSER_INTERFACE
437 static void on_link_clicked(GtkButton *button, gpointer data) {
438 appdata_t *appdata = (appdata_t*)data;
439 char *url = g_object_get_data(G_OBJECT(button), "url");
440 if(url) browser_url(appdata, url);
441 }
442 #endif
443
444 /* a button containing a weblink */
445 GtkWidget *link_button_attrib(appdata_t *appdata, char *str, char *url,
446 int size, int strikethrough) {
447
448 #ifdef ENABLE_BROWSER_INTERFACE
449 if(url) {
450 GtkWidget *button = gtk_button_attrib(str, size, strikethrough);
451 g_object_set_data(G_OBJECT(button), "url", url);
452 gtk_signal_connect(GTK_OBJECT(button), "clicked",
453 (GtkSignalFunc)on_link_clicked, appdata);
454
455 return button;
456 }
457 #endif
458 return gtk_label_attrib(str, size, strikethrough);
459 }
460
461 #ifdef ENABLE_BROWSER_INTERFACE
462 static void on_link_id_clicked(GtkButton *button, gpointer data) {
463 appdata_t *appdata = (appdata_t*)data;
464
465 unsigned int id = (unsigned int)g_object_get_data(G_OBJECT(button), "id");
466 char *type = g_object_get_data(G_OBJECT(button), "type");
467
468 char *url = g_strdup_printf("http://www.geocaching.com/%s?id=%u",
469 type, id);
470
471 if(url) {
472 browser_url(appdata, url);
473 g_free(url);
474 }
475 }
476 #endif
477
478 GtkWidget *link_button_by_id(appdata_t *appdata, char *str,
479 const char *type, int id) {
480
481 #ifdef ENABLE_BROWSER_INTERFACE
482 if(id) {
483 GtkWidget *ref = gtk_button_new_with_label(str);
484 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
485 // hildon_gtk_widget_set_theme_size(ref,
486 // (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
487 #endif
488 g_object_set_data(G_OBJECT(ref), "id", (gpointer)id);
489 g_object_set_data(G_OBJECT(ref), "type", (gpointer)type);
490 gtk_signal_connect(GTK_OBJECT(ref), "clicked",
491 GTK_SIGNAL_FUNC(on_link_id_clicked), appdata);
492
493 return ref;
494 }
495 #endif
496 return gtk_label_new(str);
497 }
498
499
500 GtkWidget *link_icon_button_by_id(appdata_t *appdata, GtkWidget *icon,
501 const char *type, int id) {
502
503 #ifdef ENABLE_BROWSER_INTERFACE
504 if(id) {
505 GtkWidget *ref = gtk_button_new();
506 gtk_button_set_image(GTK_BUTTON(ref), icon);
507
508 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
509 // hildon_gtk_widget_set_theme_size(ref,
510 // (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
511 #endif
512 g_object_set_data(G_OBJECT(ref), "id", (gpointer)id);
513 g_object_set_data(G_OBJECT(ref), "type", (gpointer)type);
514 gtk_signal_connect(GTK_OBJECT(ref), "clicked",
515 GTK_SIGNAL_FUNC(on_link_id_clicked), appdata);
516
517 return ref;
518 }
519 #endif
520 return icon;
521 }
522
523 /* left aligned, word wrapped multiline widget */
524 GtkWidget *simple_text_widget(char *text) {
525 GtkWidget *label = gtk_label_new(text);
526
527 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
528 gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD);
529 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
530
531 return label;
532 }
533
534
535 /* a label that is left aligned */
536 GtkWidget *left_label_new(char *str) {
537 GtkWidget *widget = gtk_label_new(str);
538 gtk_misc_set_alignment(GTK_MISC(widget), 0.0f, 0.5f);
539 return widget;
540 }
541
542 static void pos_set(GtkWidget *item, float lat, float lon) {
543 char str[32];
544
545 pos_lat_str(str, sizeof(str)-1, lat);
546 GtkWidget *lat_entry = g_object_get_data(G_OBJECT(item), "lat_entry");
547 gtk_entry_set_text(GTK_ENTRY(lat_entry), str);
548
549 pos_lon_str(str, sizeof(str)-1, lon);
550 GtkWidget *lon_entry = g_object_get_data(G_OBJECT(item), "lon_entry");
551 gtk_entry_set_text(GTK_ENTRY(lon_entry), str);
552 }
553
554 static void cb_gps(GtkWidget *item, gpointer data) {
555 appdata_t *appdata = (appdata_t*)data;
556 gint id = (gint)g_object_get_data(G_OBJECT(item), "id");
557 pos_t *pos = NULL;
558
559 if(!id)
560 pos = gps_get_pos(appdata);
561 else if(id == 1)
562 pos = &appdata->home;
563 else {
564 location_t *location = appdata->location;
565 while(location && id > 2) {
566 location = location->next;
567 id--;
568 }
569
570 if(id == 2)
571 pos = &location->pos;
572 }
573
574 if(!pos) pos_set(item, NAN, NAN);
575 else pos_set(item, pos->lat, pos->lon);
576 }
577
578 static void cb_geomath(GtkWidget *item, gpointer data) {
579 appdata_t *appdata = (appdata_t*)data;
580
581 pos_set(item, appdata->geomath.lat, appdata->geomath.lon);
582 }
583
584 #ifdef ENABLE_OSM_GPS_MAP
585 static void cb_map(GtkWidget *item, gpointer data) {
586 appdata_t *appdata = (appdata_t*)data;
587
588 pos_set(item, appdata->map.pos.lat, appdata->map.pos.lon);
589 }
590 #endif
591
592 static void cb_cache(GtkWidget *item, gpointer data) {
593 appdata_t *appdata = (appdata_t*)data;
594
595 cache_t *cache = appdata->cur_cache;
596 g_assert(cache);
597
598 gint id = (gint)g_object_get_data(G_OBJECT(item), "id");
599
600 if(!id)
601 pos_set(item, cache->pos.lat, cache->pos.lon);
602 else {
603 wpt_t *wpt = cache->wpt;
604 while(wpt && id > 1) {
605 wpt = wpt->next;
606 id--;
607 }
608
609 if(id == 1)
610 pos_set(item, wpt->pos.lat, wpt->pos.lon);
611 }
612 }
613
614 #ifndef PICKER_DIALOG
615 static GtkWidget *menu_add(GtkWidget *menu, appdata_t *appdata,
616 GtkWidget *icon, char *menu_str,
617 void(*func)(GtkWidget*, gpointer), gint id,
618 GtkWidget *lon_entry, GtkWidget *lat_entry) {
619
620 GtkWidget *item = gtk_image_menu_item_new_with_label(menu_str);
621
622 if(icon)
623 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), icon);
624
625 g_object_set_data(G_OBJECT(item), "lat_entry", (gpointer)lat_entry);
626 g_object_set_data(G_OBJECT(item), "lon_entry", (gpointer)lon_entry);
627 g_object_set_data(G_OBJECT(item), "id", (gpointer)id);
628
629 if(func)
630 gtk_signal_connect(GTK_OBJECT(item), "activate",
631 (GtkSignalFunc)func, appdata);
632
633 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
634
635 return item;
636 }
637
638 static GtkWidget *popup_menu_create(appdata_t *appdata,
639 GtkWidget *lat_entry, GtkWidget *lon_entry) {
640 GtkWidget *menu = gtk_menu_new();
641
642 menu_add(menu, appdata, icon_get_widget(ICON_POS, 18),
643 _("GPS position"), cb_gps, 0, lon_entry, lat_entry);
644
645 menu_add(menu, appdata, icon_get_widget(ICON_POS, 21),
646 _("Home"), cb_gps, 1, lon_entry, lat_entry);
647
648 location_t *location = appdata->location;
649 gint id = 2;
650 while(location) {
651 menu_add(menu, appdata, icon_get_widget(ICON_POS, 21),
652 location->name, cb_gps, id++, lon_entry, lat_entry);
653
654 location = location->next;
655 }
656
657 menu_add(menu, appdata, icon_get_widget(ICON_POS, 19),
658 _("Geomath projection"), cb_geomath, 0, lon_entry, lat_entry);
659 #ifdef ENABLE_OSM_GPS_MAP
660 menu_add(menu, appdata, icon_get_widget(ICON_POS, 20),
661 _("Map position"), cb_map, 0, lon_entry, lat_entry);
662 #endif
663
664 if(appdata->cur_cache) {
665 cache_t *cache = appdata->cur_cache;
666
667 char *name = cache->name;
668 if(!name) name = cache->id;
669
670 if(!isnan(cache->pos.lat) && !isnan(cache->pos.lon)) {
671 menu_add(menu, appdata, icon_get_widget(ICON_POS, cache->type + 6),
672 name, cb_cache, 0, lon_entry, lat_entry);
673 }
674
675 wpt_t *wpt = cache->wpt;
676 gint id = 1;
677 while(wpt) {
678 GtkWidget *icon = NULL;
679 if(wpt->sym != WPT_SYM_UNKNOWN)
680 icon = icon_get_widget(ICON_POS, wpt->sym);
681
682 char *name = wpt->desc;
683 if(!name) name = wpt->cmt;
684 if(!name) name = wpt->id;
685
686 menu_add(menu, appdata, icon, name, cb_cache, id++,
687 lon_entry, lat_entry);
688
689 wpt = wpt->next;
690 }
691 }
692
693 gtk_widget_show_all(menu);
694
695 return menu;
696 }
697
698 static gint on_popup_button_press(GtkWidget *button, GdkEventButton *event,
699 gpointer data) {
700
701 if(event->type == GDK_BUTTON_PRESS) {
702 GtkWidget *menu = g_object_get_data(G_OBJECT(button), "menu");
703
704 /* draw a popup menu */
705 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
706 event->button, event->time);
707 return TRUE;
708 }
709 return FALSE;
710 }
711
712 static void on_popup_destroy(GtkWidget *widget, gpointer user_data ) {
713 GtkWidget *menu = g_object_get_data(G_OBJECT(widget), "menu");
714 gtk_widget_destroy(menu);
715 }
716 #endif
717
718 #ifdef PICKER_DIALOG
719
720 enum {
721 PICKER_COL_ICON = 0,
722 PICKER_COL_NAME,
723 PICKER_COL_ID,
724 PICKER_COL_CB,
725 PICKER_NUM_COLS
726 };
727
728 static void picker_add(GtkListStore *store, appdata_t *appdata,
729 GdkPixbuf *icon, char *menu_str,
730 void(*func)(GtkWidget*, gpointer), gint id) {
731 GtkTreeIter iter;
732
733 /* Append a row and fill in some data */
734 gtk_list_store_append (store, &iter);
735
736 gtk_list_store_set(store, &iter,
737 PICKER_COL_ICON, icon,
738 PICKER_COL_NAME, menu_str,
739 PICKER_COL_ID, id,
740 PICKER_COL_CB, func,
741 -1);
742 }
743
744 static void on_picker_activated(GtkTreeView *treeview,
745 GtkTreePath *path,
746 GtkTreeViewColumn *col,
747 gpointer userdata) {
748 GtkTreeIter iter;
749 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
750
751 if(gtk_tree_model_get_iter(model, &iter, path)) {
752 gint id;
753 void(*func)(GtkWidget*, gpointer);
754 gtk_tree_model_get(model, &iter,
755 PICKER_COL_ID, &id,
756 PICKER_COL_CB, &func,
757 -1);
758
759 /* set id on widget as callbacks expect it this way */
760 g_object_set_data(G_OBJECT(treeview), "id", (gpointer)id);
761 func(GTK_WIDGET(treeview), userdata);
762
763 /* xyz */
764 gtk_dialog_response(GTK_DIALOG(gtk_widget_get_toplevel(
765 GTK_WIDGET(treeview))), GTK_RESPONSE_ACCEPT);
766
767 }
768 }
769
770 static GtkWidget *picker_create(appdata_t *appdata,
771 GtkWidget *lat_entry, GtkWidget *lon_entry) {
772 GtkCellRenderer *renderer;
773 GtkListStore *store;
774
775 GtkWidget *view = gtk_tree_view_new();
776
777 g_object_set_data(G_OBJECT(view), "lat_entry", (gpointer)lat_entry);
778 g_object_set_data(G_OBJECT(view), "lon_entry", (gpointer)lon_entry);
779
780 /* --- "Icon" column --- */
781 renderer = gtk_cell_renderer_pixbuf_new();
782 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
783 -1, "Icon", renderer, "pixbuf", PICKER_COL_ICON, NULL);
784
785 /* --- "Name" column --- */
786 renderer = gtk_cell_renderer_text_new();
787 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
788 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
789 "Name", renderer, "text", PICKER_COL_NAME, NULL);
790 gtk_tree_view_column_set_expand(column, TRUE);
791 gtk_tree_view_insert_column(GTK_TREE_VIEW(view), column, -1);
792
793 store = gtk_list_store_new(PICKER_NUM_COLS,
794 GDK_TYPE_PIXBUF,
795 G_TYPE_STRING,
796 G_TYPE_INT,
797 G_TYPE_POINTER);
798
799 picker_add(store, appdata, icon_get(ICON_POS, 18),
800 _("GPS position"), cb_gps, 0);
801
802 picker_add(store, appdata, icon_get(ICON_POS, 21),
803 _("Home"), cb_gps, 1);
804
805 location_t *location = appdata->location;
806 gint id = 2;
807 while(location) {
808 picker_add(store, appdata, icon_get(ICON_POS, 21),
809 location->name, cb_gps, id++);
810
811 location = location->next;
812 }
813
814 picker_add(store, appdata, icon_get(ICON_POS, 19),
815 _("Geomath projection"), cb_geomath, 0);
816 #ifdef ENABLE_OSM_GPS_MAP
817 picker_add(store, appdata, icon_get(ICON_POS, 20),
818 _("Map position"), cb_map, 0);
819 #endif
820
821 if(appdata->cur_cache) {
822 cache_t *cache = appdata->cur_cache;
823
824 char *name = cache->name;
825 if(!name) name = cache->id;
826
827 if(!isnan(cache->pos.lat) && !isnan(cache->pos.lon)) {
828 picker_add(store, appdata, icon_get(ICON_POS, cache->type + 6),
829 name, cb_cache, 0);
830 }
831
832 wpt_t *wpt = cache->wpt;
833 gint id = 1;
834 while(wpt) {
835 GdkPixbuf *icon = NULL;
836 if(wpt->sym != WPT_SYM_UNKNOWN)
837 icon = icon_get(ICON_POS, wpt->sym);
838
839 char *name = wpt->desc;
840 if(!name) name = wpt->cmt;
841 if(!name) name = wpt->id;
842
843 picker_add(store, appdata, icon, name, cb_cache, id++);
844 wpt = wpt->next;
845 }
846 }
847
848
849 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
850 g_object_unref(store);
851
852 /* make list react on clicks */
853 g_signal_connect(view, "row-activated",
854 (GCallback)on_picker_activated, appdata);
855
856 #if 0
857 g_signal_connect(view, "destroy",
858 (GCallback)cachelist_destroy, ce);
859 #endif
860
861 /* put this inside a scrolled view */
862 #ifndef USE_PANNABLE_AREA
863 GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
864 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
865 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
866 gtk_container_add(GTK_CONTAINER(scrolled_window), view);
867 return scrolled_window;
868 #else
869 GtkWidget *pannable_area = hildon_pannable_area_new();
870 gtk_container_add(GTK_CONTAINER(pannable_area), view);
871 return pannable_area;
872 #endif
873 }
874
875 static gint on_picker_button_press(GtkWidget *button,
876 GdkEventButton *event, gpointer data) {
877 appdata_t *appdata = (appdata_t*)data;
878
879 gpointer lat_entry = g_object_get_data(G_OBJECT(button), "lat_entry");
880 gpointer lon_entry = g_object_get_data(G_OBJECT(button), "lon_entry");
881
882 if(event->type == GDK_BUTTON_PRESS) {
883 GtkWidget *dialog =
884 gtk_dialog_new_with_buttons(_("Preset coordinates"),
885 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
886 GTK_DIALOG_MODAL,
887 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
888 NULL);
889
890 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 200);
891
892 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
893 picker_create(appdata, lat_entry, lon_entry));
894
895 gtk_widget_show_all(dialog);
896 gtk_dialog_run(GTK_DIALOG(dialog));
897 gtk_widget_destroy(dialog);
898
899 return TRUE;
900 }
901 return FALSE;
902 }
903 #endif
904
905 GtkWidget *coo_popup(appdata_t *appdata,
906 GtkWidget *lat_entry, GtkWidget *lon_entry) {
907
908 GtkWidget *button = gtk_button_new();
909
910 gtk_button_set_image(GTK_BUTTON(button), icon_get_widget(ICON_POS, 17));
911
912 gtk_widget_set_tooltip_text(button, _("Preset coordinates"));
913
914 #ifndef PICKER_DIALOG
915 gtk_signal_connect(GTK_OBJECT(button), "button-press-event",
916 (GtkSignalFunc)on_popup_button_press, appdata);
917
918 gtk_signal_connect(GTK_OBJECT(button), "destroy",
919 (GtkSignalFunc)on_popup_destroy, appdata);
920
921 g_object_set_data(G_OBJECT(button), "menu",
922 popup_menu_create(appdata, lat_entry, lon_entry));
923 #else
924 #ifdef FREMANTLE
925 hildon_gtk_widget_set_theme_size(button,
926 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
927 #endif
928
929 g_object_set_data(G_OBJECT(button), "lat_entry", (gpointer)lat_entry);
930 g_object_set_data(G_OBJECT(button), "lon_entry", (gpointer)lon_entry);
931
932 gtk_signal_connect(GTK_OBJECT(button), "button-press-event",
933 (GtkSignalFunc)on_picker_button_press, appdata);
934 #endif
935
936 return button;
937 }
938
939 GtkWidget *entry_new(void) {
940 #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
941 return gtk_entry_new();
942 #else
943 return hildon_entry_new(HILDON_SIZE_AUTO);
944 #endif
945 }
946
947 gboolean pos_differ(pos_t *pos1, pos_t *pos2) {
948 int lat1 = (60000 * pos1->lat)+0.5, lon1 = (60000 * pos1->lon)+0.5;
949 int lat2 = (60000 * pos2->lat)+0.5, lon2 = (60000 * pos2->lon)+0.5;
950
951 return((lat1 != lat2) || (lon1 != lon2));
952 }
953