Contents of /trunk/src/misc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 240 - (show annotations)
Sat Dec 12 20:56:23 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 63795 byte(s)
Multiple position handling bug fixes
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 /* TODO:
21 */
22
23 #include <math.h>
24 #include <string.h>
25 #include <ctype.h>
26
27 #include <glib.h>
28 #include <glib/gstdio.h>
29
30 #include "gpxview.h"
31
32 #ifdef ENABLE_OSM_GPS_MAP
33 #include "osm-gps-map.h"
34 #endif
35
36 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR >= 5)
37 #include <hildon/hildon-entry.h>
38 #include <hildon/hildon-touch-selector.h>
39 #include <hildon/hildon-picker-button.h>
40 #include <hildon/hildon-picker-dialog.h>
41 #include <hildon/hildon-check-button.h>
42 #endif
43
44 float roundf(float x);
45
46 /* Enable special fremantle UI elements. These particular widgets */
47 /* don't use any hildon/fremantle specific parts and can thus even */
48 /* be used under plain gtk. This is mainly for testing and developemt */
49 #ifdef FREMANTLE
50 #define PRESET_PICKER_DIALOG
51 #define COORDINATE_PICKER
52 #endif
53
54 char strlastchr(char *str) {
55 return str[strlen(str)]-1;
56 }
57
58 /* make sure the entire path "dir" exists and create it if not */
59 int checkdir(char *dir) {
60 struct stat filestat;
61 char *p = dir, tmp;
62
63 /* don't try to create root dir */
64 if(p[0] == '/') p++;
65
66 do {
67 while(*p && *p != '/') p++;
68 tmp = *p;
69 *p = 0;
70
71 int err = stat(dir, &filestat);
72 if(err) {
73 if(mkdir(dir, S_IRWXU) != 0) {
74 perror("mkdir()");
75 *p++ = tmp;
76 return -1;
77 }
78 } else {
79 if(!filestat.st_mode & S_IFDIR) {
80 printf("File %s exists and is _no_ directory\n", dir);
81 *p++ = tmp;
82 return -1;
83 }
84 }
85
86 *p++ = tmp;
87 } while(tmp && strchr(p, '/'));
88
89 return 0;
90 }
91
92 void pos_lat_str(char *str, int len, float latitude) {
93 char *c = _("N");
94 float integral, fractional;
95
96 if(isnan(latitude))
97 str[0] = 0;
98 else {
99 if(latitude < 0) { latitude = fabs(latitude); c = _("S"); }
100 fractional = modff(latitude, &integral);
101
102 snprintf(str, len, "%s %02d° %06.3f'", c, (int)integral, fractional*60.0);
103 }
104 }
105
106 GtkWidget *pos_lat(float latitude, int size, int strikethrough) {
107 char str[32];
108
109 pos_lat_str(str, sizeof(str), latitude);
110 return gtk_label_attrib(str, size, strikethrough);
111 }
112
113 void pos_lon_str(char *str, int len, float longitude) {
114 char *c = _("E");
115 float integral, fractional;
116
117 if(isnan(longitude))
118 str[0] = 0;
119 else {
120 if(longitude < 0) { longitude = fabs(longitude); c = _("W"); }
121 fractional = modff(longitude, &integral);
122
123 snprintf(str, len, "%s %03d° %06.3f'", c, (int)integral, fractional*60.0);
124 }
125 }
126
127 GtkWidget *pos_lon(float longitude, int size, int strikethrough) {
128 char str[32];
129
130 pos_lon_str(str, sizeof(str), longitude);
131 return gtk_label_attrib(str, size, strikethrough);
132 }
133
134 float pos_parse_lat(char *str) {
135 int integral_int;
136 float fractional;
137 char c;
138
139 if(sscanf(str, "%c %d° %f'", &c, &integral_int, &fractional) == 3) {
140 c = toupper(c);
141
142 if(c != 'S' && c != 'N')
143 return NAN;
144
145 /* prevent -0.0 */
146 if(!integral_int && (fractional == 0.0))
147 return 0.0;
148
149 return ((c == 'S')?-1:+1) * (integral_int + fractional/60.0);
150 }
151
152 return NAN;
153 }
154
155 float pos_parse_lon(char *str) {
156 int integral_int;
157 float fractional;
158 char c;
159
160 if(sscanf(str, "%c %d° %f'", &c, &integral_int, &fractional) == 3) {
161 c = toupper(c);
162
163 /* O is german "Ost" for "East" */
164 if(c != 'E' && c != 'W' && c != 'O')
165 return NAN;
166
167 /* prevent -0.0 */
168 if(!integral_int && (fractional == 0.0))
169 return 0.0;
170
171 return ((c == 'W')?-1:+1) * (integral_int + fractional/60.0);
172 }
173
174 return NAN;
175 }
176
177 const char *pos_get_bearing_str(pos_t from, pos_t to) {
178 static const char *bear_str[]={
179 "N", "NE", "E", "SE", "S", "SW", "W", "NW", "" };
180
181 float bearing = gpx_pos_get_bearing(from, to);
182 if(!isnan(bearing)) {
183 int idx = (bearing+22.5)/45.0;
184 /* make sure we stay in icon bounds */
185 while(idx < 0) idx += 8;
186 while(idx > 7) idx -= 8;
187 return _(bear_str[idx]);
188 }
189
190 return bear_str[8]; // empty string
191 }
192
193 /* the maemo font size is quite huge, so we adjust some fonts */
194 /* differently on maemo and non-maemo. Basically "BIG" does nothing */
195 /* on maemo and "SMALL" only does something on maemo */
196 #ifdef USE_MAEMO
197 #define MARKUP_SMALL "<span size='small'>%s</span>"
198 GtkWidget *gtk_label_small(char *str) {
199 GtkWidget *label = gtk_label_new("");
200 char *markup = g_markup_printf_escaped(MARKUP_SMALL, str);
201 gtk_label_set_markup(GTK_LABEL(label), markup);
202 g_free(markup);
203 return label;
204 }
205 #else
206 #define MARKUP_BIG "<span size='x-large'>%s</span>"
207 GtkWidget *gtk_label_big(char *str) {
208 GtkWidget *label = gtk_label_new("");
209 char *markup = g_markup_printf_escaped(MARKUP_BIG, str);
210 gtk_label_set_markup(GTK_LABEL(label), markup);
211 g_free(markup);
212 return label;
213 }
214 #endif
215
216 void gtk_label_attrib_set(GtkWidget *label,
217 char *str, int size, int strikethrough) {
218 char format[80];
219
220 snprintf(format, sizeof(format), "<span%s%s%s>%%s</span>",
221 #ifdef USE_MAEMO
222 (size==SIZE_SMALL)?" size='small'":"",
223 #else
224 (size==SIZE_BIG)?" size='x-large'":"",
225 #endif
226 strikethrough?" strikethrough='yes'":"",
227 (strikethrough==STRIKETHROUGH_RED)?" strikethrough_color='red'":"");
228
229 char *markup = g_markup_printf_escaped(format, str);
230 // printf("markup = %s\n", markup);
231 gtk_label_set_markup(GTK_LABEL(label), markup);
232 g_free(markup);
233 }
234
235 GtkWidget *gtk_label_attrib(char *str, int size, int strikethrough) {
236 GtkWidget *label = gtk_label_new("");
237 gtk_label_attrib_set(label, str, size, strikethrough);
238 return label;
239 }
240
241 GtkWidget *gtk_button_attrib(char *str, int size, int strikethrough) {
242 GtkWidget *button = gtk_button_new_with_label("");
243 gtk_label_attrib_set(gtk_bin_get_child(GTK_BIN(button)),
244 str, size, strikethrough);
245 return button;
246 }
247
248 void textbox_disable(GtkWidget *widget) {
249 gtk_editable_set_editable(GTK_EDITABLE(widget), FALSE);
250 gtk_widget_set_sensitive(widget, FALSE);
251 }
252
253 void textbox_enable(GtkWidget *widget) {
254 gtk_widget_set_sensitive(widget, TRUE);
255 gtk_editable_set_editable(GTK_EDITABLE(widget), TRUE);
256 }
257
258 pos_t *get_pos(appdata_t *appdata) {
259 pos_t *pos = &appdata->home;
260
261 if(appdata->active_location) {
262 int i = appdata->active_location-1;
263 location_t *loc = appdata->location;
264 while(i--) loc = loc->next;
265 pos = &loc->pos;
266 }
267
268 if(appdata->use_gps) {
269 pos = gps_get_pos(appdata);
270
271 if(!pos) pos = &appdata->gps; /* use saved position */
272 else appdata->gps = *pos; /* save position */
273 }
274 return pos;
275 }
276
277 void distance_str(char *str, int len, float dist, gboolean imperial) {
278 if(isnan(dist))
279 snprintf(str, len, "---");
280 else if(imperial) {
281 /* 1 mil = 1760 yd = 5280 ft ... */
282 if(dist<0.018) snprintf(str, len, "%.1f ft", dist*5280.0);
283 else if(dist<0.055) snprintf(str, len, "%.1f yd", dist*1760.0);
284 else if(dist<0.55) snprintf(str, len, "%.0f yd", dist*1760.0);
285 else if(dist<10.0) snprintf(str, len, "%.2f mi", dist);
286 else if(dist<100.0) snprintf(str, len, "%.1f mi", dist);
287 else snprintf(str, len, "%.0f mi", dist);
288 } else {
289 if(dist<0.01) snprintf(str, len, "%.2f m", dist*1000.0);
290 else if(dist<0.1) snprintf(str, len, "%.1f m", dist*1000.0);
291 else if(dist<1.0) snprintf(str, len, "%.0f m", dist*1000.0);
292 else if(dist<100.0) snprintf(str, len, "%.1f km", dist);
293 else snprintf(str, len, "%.0f km", dist);
294 }
295 }
296
297 /* return distance in miles or kilometers */
298 float distance_parse(char *str, gboolean imperial) {
299 char unit[4];
300 float val = NAN;
301
302 if(sscanf(str, "%f %3s", &val, unit) == 2) {
303 gboolean fimp = FALSE;
304
305 if(strcasecmp(unit, "ft") == 0) { fimp = TRUE; val /= 5280.0; }
306 else if(strcasecmp(unit, "yd") == 0) { fimp = TRUE; val /= 1760.0; }
307 else if(strcasecmp(unit, "mi") == 0) { fimp = TRUE; }
308 else if(strcasecmp(unit, "m") == 0) { fimp = FALSE; val /= 1000.0; }
309 else if(strcasecmp(unit, "km") == 0) { fimp = FALSE; }
310 else val = NAN;
311
312 /* found imperial and metric requested? convert miles into kilometers */
313 if(fimp & !imperial) val *= 1.609344;
314
315 /* found metric and imperial requested? convert kilometers into miles */
316 if(!fimp & imperial) val /= 1.609344;
317 }
318 return val;
319 }
320
321 /* ------------------ coordinate picker tool --------------------------- */
322
323 #ifndef COORDINATE_PICKER
324 static gboolean mark(GtkWidget *widget, gboolean valid) {
325 gtk_widget_set_state(widget, valid?GTK_STATE_NORMAL:TAG_STATE);
326 return valid;
327 }
328
329 GtkWidget *red_entry_new_with_text(char *str) {
330 GdkColor color;
331
332 GtkWidget *widget = entry_new();
333 gdk_color_parse("#ff0000", &color);
334 gtk_widget_modify_text(widget, TAG_STATE, &color);
335 if(str) gtk_entry_set_text(GTK_ENTRY(widget), str);
336
337 return widget;
338 }
339
340 static void callback_modified_lat(GtkWidget *widget, gpointer data ) {
341 float i = pos_parse_lat((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
342 mark(widget, !isnan(i));
343 }
344 #else
345 static void picker_center_iter(GtkWidget *view, GtkTreeIter *iter) {
346 /* select new iter */
347 GtkTreeSelection *selection =
348 gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
349 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
350 gtk_tree_selection_select_iter(selection, iter);
351 GtkTreePath *path =
352 gtk_tree_model_get_path(model, iter);
353 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view),
354 path, NULL, TRUE, 0.5, 0.5);
355 gtk_tree_path_free(path);
356 }
357
358 static void on_picker_activated(GtkTreeView *treeview,
359 GtkTreePath *path,
360 GtkTreeViewColumn *col,
361 gpointer userdata) {
362 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview),
363 path, NULL, TRUE, 0.5, 0.5);
364 }
365
366 #define DUMMY_NUM 1
367
368 static void dummy_append(GtkListStore *store, int n) {
369 GtkTreeIter iter;
370
371 while(n--) {
372 gtk_list_store_append (store, &iter);
373 gtk_list_store_set(store, &iter, 0, "", 1, -1, 2, FALSE, -1);
374 }
375 }
376
377 static GtkWidget *string_picker_create(const char *str[], int sel) {
378 #ifndef FREMANTLE
379 GtkWidget *view = gtk_tree_view_new();
380 #else
381 GtkWidget *view = hildon_gtk_tree_view_new(HILDON_UI_MODE_EDIT);
382 #endif
383
384 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
385 GtkTreeSelection *selection =
386 gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
387
388 /* --- "char" column --- */
389 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
390 g_object_set(renderer, "xalign", 0.5, NULL );
391 gtk_tree_view_insert_column_with_attributes(
392 GTK_TREE_VIEW(view), -1, "str", renderer,
393 "text", 0,
394 "sensitive", 2,
395 NULL);
396
397 GtkListStore *store =
398 gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
399
400 dummy_append(store, DUMMY_NUM);
401
402 /* add strings */
403 GtkTreeIter iter, siter;
404 int i;
405 for(i=0;*str;str++,i++) {
406 gtk_list_store_append (store, &iter);
407 gtk_list_store_set(store, &iter, 0, _(*str), 1, i, 2, TRUE, -1);
408 if(i == sel) siter = iter;
409 }
410
411 dummy_append(store, DUMMY_NUM);
412
413 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
414 g_object_unref(store);
415
416 #ifndef FREMANTLE
417 g_signal_connect(view, "row-activated",
418 (GCallback)on_picker_activated, NULL);
419 #else
420 g_signal_connect(view, "hildon-row-tapped",
421 (GCallback)on_picker_activated, NULL);
422 #endif
423
424 /* select right character */
425 gtk_tree_selection_select_iter(selection, &siter);
426 picker_center_iter(view, &siter);
427
428 /* put this inside a scrolled view */
429 #ifndef USE_PANNABLE_AREA
430 GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
431 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
432 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
433 gtk_container_add(GTK_CONTAINER(scrolled_window), view);
434 return scrolled_window;
435 #else
436 GtkWidget *pannable_area = hildon_pannable_area_new();
437 gtk_container_add(GTK_CONTAINER(pannable_area), view);
438 return pannable_area;
439 #endif
440 }
441
442 static int picker_get(GtkWidget *widget) {
443 GtkWidget *view = gtk_bin_get_child(GTK_BIN(widget));
444
445 GtkTreeSelection *sel =
446 gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
447
448 GtkTreeModel *model;
449 GtkTreeIter iter;
450
451 /* there should never be an unseletced column. But if */
452 /* it is, we count it as zero */
453 if(!gtk_tree_selection_get_selected(sel, &model, &iter))
454 return 0;
455
456 int retval = 0;
457 gtk_tree_model_get(model, &iter, 1, &retval, -1);
458 return retval;
459 }
460
461 static GtkWidget *digit_picker_create(int min, int max, int sel) {
462 #ifndef FREMANTLE
463 GtkWidget *view = gtk_tree_view_new();
464 #else
465 GtkWidget *view = hildon_gtk_tree_view_new(HILDON_UI_MODE_EDIT);
466 #endif
467
468 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
469 GtkTreeSelection *selection =
470 gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
471
472 /* --- "digit" column --- */
473 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
474 g_object_set(renderer, "xalign", 0.5, NULL );
475 gtk_tree_view_insert_column_with_attributes(
476 GTK_TREE_VIEW(view), -1, "digit", renderer,
477 "text", 0,
478 "sensitive", 2,
479 NULL);
480
481 GtkListStore *store =
482 gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
483
484 dummy_append(store, DUMMY_NUM);
485
486 /* add digits */
487 int i;
488 GtkTreeIter siter;
489 for(i=min;i<=max;i++) {
490 char str[2] = { '0'+i, 0 };
491 GtkTreeIter iter;
492 /* Append a row and fill in some data */
493 gtk_list_store_append (store, &iter);
494 gtk_list_store_set(store, &iter, 0, str, 1, i, 2, TRUE, -1);
495
496 if(i == sel) siter = iter;
497 }
498
499 dummy_append(store, DUMMY_NUM);
500
501 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
502 g_object_unref(store);
503
504 #ifndef FREMANTLE
505 g_signal_connect(view, "row-activated",
506 (GCallback)on_picker_activated, NULL);
507 #else
508 g_signal_connect(view, "hildon-row-tapped",
509 (GCallback)on_picker_activated, NULL);
510 #endif
511
512 gtk_tree_selection_select_iter(selection, &siter);
513 picker_center_iter(view, &siter);
514
515 /* put this inside a scrolled view */
516 #ifndef USE_PANNABLE_AREA
517 GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
518 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
519 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
520 gtk_container_add(GTK_CONTAINER(scrolled_window), view);
521 return scrolled_window;
522 #else
523 GtkWidget *pannable_area = hildon_pannable_area_new();
524 gtk_container_add(GTK_CONTAINER(pannable_area), view);
525 return pannable_area;
526 #endif
527 }
528
529 static gint on_lat_picker_button_press(GtkWidget *button,
530 GdkEventButton *event, gpointer data) {
531
532 if(event->type == GDK_BUTTON_PRESS) {
533 GtkWidget *dialog =
534 gtk_dialog_new_with_buttons(_("Latitude"),
535 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
536 GTK_DIALOG_MODAL,
537 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
538 _("Done"), GTK_RESPONSE_ACCEPT,
539 NULL);
540
541 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 210);
542
543 int i, lat = (int)g_object_get_data(G_OBJECT(button), "latitude");
544
545 /* parse latitude into components */
546 int sign = (lat>=0)?0:1;
547 lat = abs(lat);
548 int deg = lat / 60000;
549 int min = (lat /1000)%60;
550 int minfrac = lat % 1000;
551
552 GtkWidget *signw, *degw[2], *minw[2], *minfracw[3];
553
554
555 /* create N/S 89° 99.999 */
556 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
557 static const char *ns[] = { "N", "S", NULL };
558 gtk_box_pack_start_defaults(GTK_BOX(hbox),
559 signw = string_picker_create(ns, sign));
560
561 gtk_box_pack_start_defaults(GTK_BOX(hbox),
562 degw[0] = digit_picker_create(0,8, deg/10));
563 gtk_box_pack_start_defaults(GTK_BOX(hbox),
564 degw[1] = digit_picker_create(0,9, deg%10));
565 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("°"), FALSE, FALSE, 0);
566
567 gtk_box_pack_start_defaults(GTK_BOX(hbox),
568 minw[0] = digit_picker_create(0,5, min/10));
569 gtk_box_pack_start_defaults(GTK_BOX(hbox),
570 minw[1] = digit_picker_create(0,9, min%10));
571 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("."), FALSE, FALSE, 0);
572
573 gtk_box_pack_start_defaults(GTK_BOX(hbox),
574 minfracw[0] = digit_picker_create(0,9, minfrac/100));
575 gtk_box_pack_start_defaults(GTK_BOX(hbox),
576 minfracw[1] = digit_picker_create(0,9, (minfrac/10)%10));
577 gtk_box_pack_start_defaults(GTK_BOX(hbox),
578 minfracw[2] = digit_picker_create(0,9, minfrac%10));
579
580 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
581
582 gtk_widget_show_all(dialog);
583 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
584
585 /* parse degrees ... */
586 for(deg=0,i=0;i<2;i++)
587 deg = 10 * deg + picker_get(degw[i]);
588
589 /* ... minutes ... */
590 for(min=0,i=0;i<2;i++)
591 min = 10 * min + picker_get(minw[i]);
592
593 /* ... and fractions of minutes */
594 for(minfrac=0,i=0;i<3;i++)
595 minfrac = 10 * minfrac + picker_get(minfracw[i]);
596
597 /* parse N/S */
598 if(deg | min | minfrac)
599 sign = picker_get(signw)?-1:1;
600 else
601 sign = 1; // theres no S 00 00.000
602
603 float latitude = sign * (deg + min/60.0 + minfrac/60000.0);
604 lat_entry_set(button, latitude);
605 }
606
607 gtk_widget_destroy(dialog);
608
609 return TRUE;
610 }
611 return FALSE;
612 }
613 #endif
614
615 /* whatever there is in the entry: if it's illegal make it */
616 #define ILLEGAL_LAT "X --° --,---'"
617 #define ILLEGAL_LON "X ---° --,---'"
618
619 static void pos_lat_check(GtkWidget *widget) {
620 if(isnan(pos_parse_lat((char*)gtk_entry_get_text(GTK_ENTRY(widget))))) {
621 #ifndef COORDINATE_PICKER
622 gtk_entry_set_text(GTK_ENTRY(widget), ILLEGAL_LAT);
623 #else
624 mark(widget, FALSE);
625 gtk_button_set_label(GTK_BUTTON(widget), ILLEGAL_LAT);
626 #endif
627 }
628 }
629
630 static void pos_lon_check(GtkWidget *widget) {
631 if(isnan(pos_parse_lon((char*)gtk_entry_get_text(GTK_ENTRY(widget))))) {
632 #ifndef COORDINATE_PICKER
633 gtk_entry_set_text(GTK_ENTRY(widget), ILLEGAL_LON);
634 #else
635 mark(widget, FALSE);
636 gtk_button_set_label(GTK_BUTTON(widget), ILLEGAL_LON);
637 #endif
638 }
639 }
640
641 /* a entry that is colored red when being "active" */
642 GtkWidget *lat_entry_new(float lat) {
643 char str[32];
644 pos_lat_str(str, sizeof(str), lat);
645
646 #ifndef COORDINATE_PICKER
647 GtkWidget *widget = red_entry_new_with_text(str);
648
649 g_signal_connect(G_OBJECT(widget), "changed",
650 G_CALLBACK(callback_modified_lat), NULL);
651
652 #else
653 GtkWidget *widget = gtk_button_new_with_label(str);
654
655 #ifdef FREMANTLE
656 hildon_gtk_widget_set_theme_size(widget,
657 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
658 #endif
659 int lat_int = (int)roundf(lat * 60000);
660 g_object_set_data(G_OBJECT(widget), "latitude", (gpointer)lat_int);
661 gtk_signal_connect(GTK_OBJECT(widget), "button-press-event",
662 (GtkSignalFunc)on_lat_picker_button_press, NULL);
663 #endif
664
665 pos_lat_check(widget);
666 return widget;
667 }
668
669 #ifndef COORDINATE_PICKER
670 static void callback_modified_lon(GtkWidget *widget, gpointer data ) {
671 float i = pos_parse_lon((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
672 mark(widget, !isnan(i));
673 }
674 #else
675 static gint on_lon_picker_button_press(GtkWidget *button,
676 GdkEventButton *event, gpointer data) {
677
678 if(event->type == GDK_BUTTON_PRESS) {
679 GtkWidget *dialog =
680 gtk_dialog_new_with_buttons(_("Longitude"),
681 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
682 GTK_DIALOG_MODAL,
683 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
684 _("Done"), GTK_RESPONSE_ACCEPT,
685 NULL);
686
687 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 210);
688
689 int i, lat = (int)g_object_get_data(G_OBJECT(button), "longitude");
690
691 /* parse latitude into components */
692 int sign = (lat>=0)?0:1;
693 lat = abs(lat);
694 int deg = lat / 60000;
695 int min = (lat /1000)%60;
696 int minfrac = lat % 1000;
697
698 GtkWidget *signw, *degw[3], *minw[2], *minfracw[3];
699
700 /* create E/W 179° 99.999 */
701 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
702 static const char *ew[] = { "E", "W", NULL };
703 gtk_box_pack_start_defaults(GTK_BOX(hbox),
704 signw = string_picker_create(ew, sign));
705
706 gtk_box_pack_start_defaults(GTK_BOX(hbox),
707 degw[0] = digit_picker_create(0,1, deg/100));
708 gtk_box_pack_start_defaults(GTK_BOX(hbox),
709 degw[1] = digit_picker_create(0,9, (deg/10)%10));
710 gtk_box_pack_start_defaults(GTK_BOX(hbox),
711 degw[2] = digit_picker_create(0,9, deg%10));
712 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("°"), FALSE, FALSE, 0);
713
714 gtk_box_pack_start_defaults(GTK_BOX(hbox),
715 minw[0] = digit_picker_create(0,5, min/10));
716 gtk_box_pack_start_defaults(GTK_BOX(hbox),
717 minw[1] = digit_picker_create(0,9, min%10));
718 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("."), FALSE, FALSE, 0);
719
720 gtk_box_pack_start_defaults(GTK_BOX(hbox),
721 minfracw[0] = digit_picker_create(0,9, minfrac/100));
722 gtk_box_pack_start_defaults(GTK_BOX(hbox),
723 minfracw[1] = digit_picker_create(0,9, (minfrac/10)%10));
724 gtk_box_pack_start_defaults(GTK_BOX(hbox),
725 minfracw[2] = digit_picker_create(0,9, minfrac%10));
726
727 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
728
729 gtk_widget_show_all(dialog);
730 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
731
732 /* parse degrees ... */
733 for(deg=0,i=0;i<3;i++)
734 deg = 10 * deg + picker_get(degw[i]);
735
736 /* ... minutes ... */
737 for(min=0,i=0;i<2;i++)
738 min = 10 * min + picker_get(minw[i]);
739
740 /* ... and fractions of minutes */
741 for(minfrac=0,i=0;i<3;i++)
742 minfrac = 10 * minfrac + picker_get(minfracw[i]);
743
744 if(deg | min | minfrac)
745 sign = picker_get(signw)?-1:1;
746 else
747 sign = 1; // theres no W 00 00.000
748
749 float longitude = sign * (deg + min/60.0 + minfrac/60000.0);
750 lon_entry_set(button, longitude);
751 }
752
753 gtk_widget_destroy(dialog);
754
755 return TRUE;
756 }
757 return FALSE;
758 }
759 #endif
760
761 /* a entry that is colored red when filled with invalid coordinate */
762 GtkWidget *lon_entry_new(float lon) {
763 char str[32];
764 pos_lon_str(str, sizeof(str), lon);
765
766 #ifndef COORDINATE_PICKER
767 GtkWidget *widget = red_entry_new_with_text(str);
768 g_signal_connect(G_OBJECT(widget), "changed",
769 G_CALLBACK(callback_modified_lon), NULL);
770
771 #else
772 GtkWidget *widget = gtk_button_new_with_label(str);
773
774 #ifdef FREMANTLE
775 hildon_gtk_widget_set_theme_size(widget,
776 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
777 #endif
778 int lon_int = (int)roundf(lon * 60000);
779 g_object_set_data(G_OBJECT(widget), "longitude", (gpointer)lon_int);
780 gtk_signal_connect(GTK_OBJECT(widget), "button-press-event",
781 (GtkSignalFunc)on_lon_picker_button_press, NULL);
782 #endif
783
784 pos_lon_check(widget);
785 return widget;
786 }
787
788 float lat_entry_get(GtkWidget *widget) {
789 #ifndef COORDINATE_PICKER
790 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
791 #else
792 char *p = (char*)gtk_button_get_label(GTK_BUTTON(widget));
793 #endif
794 return pos_parse_lat(p);
795 }
796
797 float lon_entry_get(GtkWidget *widget) {
798 #ifndef COORDINATE_PICKER
799 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
800 #else
801 char *p = (char*)gtk_button_get_label(GTK_BUTTON(widget));
802 #endif
803 return pos_parse_lon(p);
804 }
805
806 void lat_entry_set(GtkWidget *widget, float lat) {
807 char str[32];
808 pos_lat_str(str, sizeof(str)-1, lat);
809 #ifndef COORDINATE_PICKER
810 gtk_entry_set_text(GTK_ENTRY(widget), str);
811 #else
812 gtk_button_set_label(GTK_BUTTON(widget), str);
813 int lat_int = (int)roundf(lat * 60000);
814 g_object_set_data(G_OBJECT(widget), "latitude", (gpointer)lat_int);
815 g_signal_emit_by_name(widget, "changed");
816 #endif
817 pos_lat_check(widget);
818 }
819
820 void lon_entry_set(GtkWidget *widget, float lon) {
821 char str[32];
822 pos_lon_str(str, sizeof(str)-1, lon);
823 #ifndef COORDINATE_PICKER
824 gtk_entry_set_text(GTK_ENTRY(widget), str);
825 #else
826 gtk_button_set_label(GTK_BUTTON(widget), str);
827 int lon_int = (int)roundf(lon * 60000);
828 g_object_set_data(G_OBJECT(widget), "longitude", (gpointer)lon_int);
829 g_signal_emit_by_name(widget, "changed");
830 #endif
831 pos_lon_check(widget);
832 }
833
834 void lat_label_set(GtkWidget *widget, float lat) {
835 char str[32];
836 pos_lat_str(str, sizeof(str)-1, lat);
837 gtk_label_set(GTK_LABEL(widget), str);
838 }
839
840 void lon_label_set(GtkWidget *widget, float lon) {
841 char str[32];
842 pos_lon_str(str, sizeof(str)-1, lon);
843 gtk_label_set(GTK_LABEL(widget), str);
844 }
845
846 void lat_label_attrib_set(GtkWidget *widget, float lat,
847 int size, int strikethrough) {
848 char str[32];
849 pos_lat_str(str, sizeof(str)-1, lat);
850 gtk_label_attrib_set(widget, str, size, strikethrough);
851 }
852
853 void lon_label_attrib_set(GtkWidget *widget, float lon,
854 int size, int strikethrough) {
855 char str[32];
856 pos_lon_str(str, sizeof(str)-1, lon);
857 gtk_label_attrib_set(widget, str, size, strikethrough);
858 }
859
860 #ifndef COORDINATE_PICKER
861 static void callback_modified_dist(GtkWidget *widget, gpointer data ) {
862 /* don't care for metric/imperial since we only want to know if this */
863 /* is parseable at all */
864 float i =
865 distance_parse((char*)gtk_entry_get_text(GTK_ENTRY(widget)), FALSE);
866 mark(widget, !isnan(i));
867 }
868 #else
869 static gint on_dist_picker_button_press(GtkWidget *button,
870 GdkEventButton *event, gpointer data) {
871
872 if(event->type == GDK_BUTTON_PRESS) {
873 GtkWidget *dialog =
874 gtk_dialog_new_with_buttons(_("Distance"),
875 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
876 GTK_DIALOG_MODAL,
877 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
878 _("Done"), GTK_RESPONSE_ACCEPT,
879 NULL);
880
881 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 210);
882
883 /* distance is given in m or ft (depending on mil) */
884 int i, dist = (int)g_object_get_data(G_OBJECT(button), "distance");
885 gboolean mil = (gboolean)g_object_get_data(G_OBJECT(button), "mil");
886 int unit = 0;
887
888 /* parse distance into components */
889 if(mil) {
890 /* 1 mil = 1760 yd = 5280 ft. 1yd = 3 ft */
891 if(dist<95) { unit = 0; dist *= 100; }
892 else if(dist<2904) { unit = 1; dist = 100 * dist / 3; }
893 else { unit = 2; dist = 5 * dist / 264; }
894 } else {
895 if(dist<1000) { unit = 3; dist *= 100; }
896 else { unit = 4; dist /= 10; }
897 }
898
899 GtkWidget *distw[4], *fracw[2], *unitw;
900
901 /* create xxxx.x unit */
902 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
903
904 gtk_box_pack_start_defaults(GTK_BOX(hbox),
905 distw[0] = digit_picker_create(0,9, (dist/100000)%10));
906 gtk_box_pack_start_defaults(GTK_BOX(hbox),
907 distw[1] = digit_picker_create(0,9, (dist/10000)%10));
908 gtk_box_pack_start_defaults(GTK_BOX(hbox),
909 distw[2] = digit_picker_create(0,9, (dist/1000)%10));
910 gtk_box_pack_start_defaults(GTK_BOX(hbox),
911 distw[3] = digit_picker_create(0,9, (dist/100)%10));
912 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" . "), FALSE, FALSE, 0);
913 gtk_box_pack_start_defaults(GTK_BOX(hbox),
914 fracw[0] = digit_picker_create(0,9, (dist/10)%10));
915 gtk_box_pack_start_defaults(GTK_BOX(hbox),
916 fracw[1] = digit_picker_create(0,9, (dist/1)%10));
917
918 static const char *units[] = { "ft", "yd", "mi", "m", "km", NULL };
919 gtk_box_pack_start_defaults(GTK_BOX(hbox),
920 unitw = string_picker_create(units, unit));
921
922 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
923
924 gtk_widget_show_all(dialog);
925 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
926
927 /* parse distance */
928 for(dist=0,i=0;i<4;i++)
929 dist = 10 * dist + picker_get(distw[i]);
930
931 for(i=0;i<2;i++)
932 dist = 10 * dist + picker_get(fracw[i]);
933
934 unit = picker_get(unitw);
935 if(unit == 0) { dist /= 100; } // ft
936 else if(unit == 1) { dist = 3*dist/100; } // yd
937 else if(unit == 2) { dist = 528*dist/10; } // mi
938 else if(unit == 3) { dist /= 100; } // m
939 else if(unit == 4) { dist *= 10; } // km
940
941 /* user may have switched between metric and imperial */
942 float distance;
943 if(unit <= 2) {
944 distance = dist / 5280.0;
945 if(!mil) distance *= 1.609344;
946 } else {
947 distance = dist / 1000.0;
948 if( mil) distance /= 1.609344;
949 }
950
951 dist_entry_set(button, distance, mil);
952 }
953
954 gtk_widget_destroy(dialog);
955
956 return TRUE;
957 }
958 return FALSE;
959 }
960 #endif
961
962 /* a entry that is colored red when filled with invalid distance */
963 GtkWidget *dist_entry_new(float dist, gboolean mil) {
964 char str[32];
965 distance_str(str, sizeof(str), dist, mil);
966
967 #ifndef COORDINATE_PICKER
968 GtkWidget *widget = red_entry_new_with_text(str);
969 g_signal_connect(G_OBJECT(widget), "changed",
970 G_CALLBACK(callback_modified_dist), NULL);
971
972 #else
973 GtkWidget *widget = gtk_button_new_with_label(str);
974
975 #ifdef FREMANTLE
976 hildon_gtk_widget_set_theme_size(widget,
977 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
978 #endif
979 int dist_int = (int)roundf(dist * 1000); // km -> m
980 if(mil) dist_int = (int)roundf(dist * 5280.0); // mi -> ft
981
982 g_object_set_data(G_OBJECT(widget), "distance", (gpointer)dist_int);
983 g_object_set_data(G_OBJECT(widget), "mil", (gpointer)mil);
984 gtk_signal_connect(GTK_OBJECT(widget), "button-press-event",
985 (GtkSignalFunc)on_dist_picker_button_press, NULL);
986 #endif
987
988 return widget;
989 }
990
991 float dist_entry_get(GtkWidget *widget, gboolean mil) {
992 #ifndef COORDINATE_PICKER
993 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
994 #else
995 char *p = (char*)gtk_button_get_label(GTK_BUTTON(widget));
996 #endif
997 return distance_parse(p, mil);
998 }
999
1000 void dist_entry_set(GtkWidget *widget, float dist, gboolean mil) {
1001 char str[32];
1002 distance_str(str, sizeof(str), dist, mil);
1003
1004 #ifndef COORDINATE_PICKER
1005 gtk_entry_set_text(GTK_ENTRY(widget), str);
1006 #else
1007 gtk_button_set_label(GTK_BUTTON(widget), str);
1008 int dist_int = (int)roundf(dist * 1000); // km -> m
1009 if(mil) dist_int = (int)roundf(dist * 5280.0); // mi -> ft
1010 g_object_set_data(G_OBJECT(widget), "distance", (gpointer)dist_int);
1011 g_object_set_data(G_OBJECT(widget), "mil", (gpointer)mil);
1012 g_signal_emit_by_name(widget, "changed");
1013 #endif
1014 }
1015
1016 #ifndef USE_MAEMO
1017 #ifdef ENABLE_BROWSER_INTERFACE
1018 #include <libgnome/gnome-url.h>
1019
1020 int browser_url(appdata_t *appdata, char *url) {
1021 /* taken from gnome-open, part of libgnome */
1022 GError *err = NULL;
1023 gnome_url_show(url, &err);
1024 return 0;
1025 }
1026 #endif
1027 #endif
1028
1029 /* recursively remove an entire file system */
1030 void rmdir_recursive(char *path) {
1031 GDir *dir = g_dir_open(path, 0, NULL);
1032 if(dir) {
1033 const char *name = g_dir_read_name(dir);
1034 while(name) {
1035 char *fullname = g_strdup_printf("%s/%s", path, name);
1036 // printf("deleting %s\n", fullname);
1037
1038 if(g_file_test(fullname, G_FILE_TEST_IS_DIR))
1039 rmdir_recursive(fullname);
1040 else if(g_file_test(fullname, G_FILE_TEST_IS_REGULAR))
1041 g_remove(fullname);
1042
1043 g_free(fullname);
1044 name = g_dir_read_name(dir);
1045 }
1046
1047 g_dir_close(dir);
1048 }
1049 g_rmdir(path);
1050 }
1051
1052 #ifdef ENABLE_BROWSER_INTERFACE
1053 static void on_link_clicked(GtkButton *button, gpointer data) {
1054 appdata_t *appdata = (appdata_t*)data;
1055 char *url = g_object_get_data(G_OBJECT(button), "url");
1056 if(url) browser_url(appdata, url);
1057 }
1058 #endif
1059
1060 /* a button containing a weblink */
1061 GtkWidget *link_button_attrib(appdata_t *appdata, char *str, char *url,
1062 int size, int strikethrough) {
1063
1064 #ifdef ENABLE_BROWSER_INTERFACE
1065 if(url) {
1066 GtkWidget *button = gtk_button_attrib(str, size, strikethrough);
1067 g_object_set_data(G_OBJECT(button), "url", url);
1068 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1069 (GtkSignalFunc)on_link_clicked, appdata);
1070
1071 return button;
1072 }
1073 #endif
1074 return gtk_label_attrib(str, size, strikethrough);
1075 }
1076
1077 #ifdef ENABLE_BROWSER_INTERFACE
1078 static void on_link_id_clicked(GtkButton *button, gpointer data) {
1079 appdata_t *appdata = (appdata_t*)data;
1080
1081 unsigned int id = (unsigned int)g_object_get_data(G_OBJECT(button), "id");
1082 char *type = g_object_get_data(G_OBJECT(button), "type");
1083
1084 char *url = g_strdup_printf("http://www.geocaching.com/%s?id=%u",
1085 type, id);
1086
1087 if(url) {
1088 browser_url(appdata, url);
1089 g_free(url);
1090 }
1091 }
1092 #endif
1093
1094 GtkWidget *link_button_by_id(appdata_t *appdata, char *str,
1095 const char *type, int id) {
1096
1097 #ifdef ENABLE_BROWSER_INTERFACE
1098 if(id) {
1099 GtkWidget *ref = gtk_button_new_with_label(str);
1100 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
1101 // hildon_gtk_widget_set_theme_size(ref,
1102 // (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1103 #endif
1104 g_object_set_data(G_OBJECT(ref), "id", (gpointer)id);
1105 g_object_set_data(G_OBJECT(ref), "type", (gpointer)type);
1106 gtk_signal_connect(GTK_OBJECT(ref), "clicked",
1107 GTK_SIGNAL_FUNC(on_link_id_clicked), appdata);
1108
1109 return ref;
1110 }
1111 #endif
1112 return gtk_label_new(str);
1113 }
1114
1115
1116 GtkWidget *link_icon_button_by_id(appdata_t *appdata, GtkWidget *icon,
1117 const char *type, int id) {
1118
1119 #ifdef ENABLE_BROWSER_INTERFACE
1120 if(id) {
1121 GtkWidget *ref = gtk_button_new();
1122 gtk_button_set_image(GTK_BUTTON(ref), icon);
1123
1124 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
1125 // hildon_gtk_widget_set_theme_size(ref,
1126 // (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1127 #endif
1128 g_object_set_data(G_OBJECT(ref), "id", (gpointer)id);
1129 g_object_set_data(G_OBJECT(ref), "type", (gpointer)type);
1130 gtk_signal_connect(GTK_OBJECT(ref), "clicked",
1131 GTK_SIGNAL_FUNC(on_link_id_clicked), appdata);
1132
1133 return ref;
1134 }
1135 #endif
1136 return icon;
1137 }
1138
1139 /* left aligned, word wrapped multiline widget */
1140 GtkWidget *simple_text_widget(char *text) {
1141 GtkWidget *label = gtk_label_new(text);
1142
1143 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1144 gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD);
1145 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1146
1147 return label;
1148 }
1149
1150
1151 /* a label that is left aligned */
1152 GtkWidget *left_label_new(char *str) {
1153 GtkWidget *widget = gtk_label_new(str);
1154 gtk_misc_set_alignment(GTK_MISC(widget), 0.0f, 0.5f);
1155 return widget;
1156 }
1157
1158 /* ------------- preset coordinate picker tool ----------------- */
1159
1160 static void pos_set(GtkWidget *item, float lat, float lon) {
1161
1162 GtkWidget *lat_entry = g_object_get_data(G_OBJECT(item), "lat_entry");
1163 lat_entry_set(lat_entry, lat);
1164
1165 GtkWidget *lon_entry = g_object_get_data(G_OBJECT(item), "lon_entry");
1166 lon_entry_set(lon_entry, lon);
1167 }
1168
1169 static void cb_gps(GtkWidget *item, gpointer data) {
1170 appdata_t *appdata = (appdata_t*)data;
1171 gint id = (gint)g_object_get_data(G_OBJECT(item), "id");
1172 pos_t *pos = NULL;
1173
1174 if(!id)
1175 pos = gps_get_pos(appdata);
1176 else if(id == 1)
1177 pos = &appdata->home;
1178 else {
1179 location_t *location = appdata->location;
1180 while(location && id > 2) {
1181 location = location->next;
1182 id--;
1183 }
1184
1185 if(id == 2)
1186 pos = &location->pos;
1187 }
1188
1189 if(!pos) pos_set(item, NAN, NAN);
1190 else pos_set(item, pos->lat, pos->lon);
1191 }
1192
1193 static void cb_geomath(GtkWidget *item, gpointer data) {
1194 appdata_t *appdata = (appdata_t*)data;
1195
1196 pos_set(item, appdata->geomath.lat, appdata->geomath.lon);
1197 }
1198
1199 #ifdef ENABLE_OSM_GPS_MAP
1200 static void cb_map(GtkWidget *item, gpointer data) {
1201 appdata_t *appdata = (appdata_t*)data;
1202
1203 pos_set(item, appdata->map.pos.lat, appdata->map.pos.lon);
1204 }
1205 #endif
1206
1207 #ifdef ENABLE_MAEMO_MAPPER
1208 static void cb_mm(GtkWidget *item, gpointer data) {
1209 appdata_t *appdata = (appdata_t*)data;
1210
1211 pos_set(item, appdata->mmpos.lat, appdata->mmpos.lon);
1212 }
1213 #endif
1214
1215 static void cb_cache(GtkWidget *item, gpointer data) {
1216 appdata_t *appdata = (appdata_t*)data;
1217
1218 cache_t *cache = appdata->cur_cache;
1219 g_assert(cache);
1220
1221 gint id = (gint)g_object_get_data(G_OBJECT(item), "id");
1222
1223 if(!id)
1224 pos_set(item, cache->pos.lat, cache->pos.lon);
1225 else if(id == 1) {
1226 /* fetch position out of notes dialog since they probably */
1227 /* haven't been saved yet */
1228 pos_t pos = notes_get_pos(appdata->cache_context);
1229 pos_set(item, pos.lat, pos.lon);
1230 } else {
1231 wpt_t *wpt = cache->wpt;
1232 while(wpt && id > 2) {
1233 wpt = wpt->next;
1234 id--;
1235 }
1236
1237 if(id == 2)
1238 pos_set(item, wpt->pos.lat, wpt->pos.lon);
1239 }
1240 }
1241
1242 #ifndef PRESET_PICKER_DIALOG
1243 static GtkWidget *menu_add(GtkWidget *menu, appdata_t *appdata,
1244 GtkWidget *icon, char *menu_str,
1245 void(*func)(GtkWidget*, gpointer), gint id,
1246 GtkWidget *lon_entry, GtkWidget *lat_entry) {
1247
1248 GtkWidget *item = gtk_image_menu_item_new_with_label(menu_str);
1249
1250 if(icon)
1251 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), icon);
1252
1253 g_object_set_data(G_OBJECT(item), "lat_entry", (gpointer)lat_entry);
1254 g_object_set_data(G_OBJECT(item), "lon_entry", (gpointer)lon_entry);
1255 g_object_set_data(G_OBJECT(item), "id", (gpointer)id);
1256
1257 if(func)
1258 gtk_signal_connect(GTK_OBJECT(item), "activate",
1259 (GtkSignalFunc)func, appdata);
1260
1261 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1262
1263 return item;
1264 }
1265
1266 static GtkWidget *popup_menu_create(appdata_t *appdata,
1267 GtkWidget *lat_entry, GtkWidget *lon_entry) {
1268 GtkWidget *menu = gtk_menu_new();
1269
1270 if(pos_valid(gps_get_pos(appdata)))
1271 menu_add(menu, appdata, icon_get_widget(ICON_POS, 18),
1272 _("GPS position"), cb_gps, 0, lon_entry, lat_entry);
1273
1274 if(pos_valid(&appdata->home))
1275 menu_add(menu, appdata, icon_get_widget(ICON_POS, 21),
1276 _("Home"), cb_gps, 1, lon_entry, lat_entry);
1277
1278 location_t *location = appdata->location;
1279 gint id = 2;
1280 while(location) {
1281 if(pos_valid(&location->pos))
1282 menu_add(menu, appdata, icon_get_widget(ICON_POS, 17),
1283 location->name, cb_gps, id, lon_entry, lat_entry);
1284
1285 id++;
1286 location = location->next;
1287 }
1288
1289 if(pos_valid(&appdata->geomath))
1290 menu_add(menu, appdata, icon_get_widget(ICON_POS, 19),
1291 _("Geomath projection"), cb_geomath, 0, lon_entry, lat_entry);
1292
1293 #ifdef ENABLE_OSM_GPS_MAP
1294 if(pos_valid(&appdata->map.pos))
1295 menu_add(menu, appdata, icon_get_widget(ICON_POS, 20), _("Map position"),
1296 cb_map, 0, lon_entry, lat_entry);
1297 #endif
1298
1299 #ifdef ENABLE_MAEMO_MAPPER
1300 if(appdata->mmpos_valid) {
1301 menu_add(menu, appdata, icon_get_widget(ICON_POS, 24),
1302 _("Maemo Mapper position"), cb_mm, 0, lon_entry, lat_entry);
1303 }
1304 #endif
1305
1306 if(appdata->cur_cache) {
1307 cache_t *cache = appdata->cur_cache;
1308
1309 char *name = cache->name;
1310 if(!name) name = cache->id;
1311
1312 /* original cache position */
1313 if(pos_valid(&cache->pos))
1314 menu_add(menu, appdata, icon_get_widget(ICON_POS, cache->type + 6),
1315 name, cb_cache, 0, lon_entry, lat_entry);
1316
1317 /* overwritten cache position */
1318 if(appdata->cache_context && notes_get_override(appdata->cache_context))
1319 menu_add(menu, appdata, icon_get_widget(ICON_POS, cache->type + 6),
1320 _("Modified coordinate"), cb_cache, 1, lon_entry, lat_entry);
1321
1322 wpt_t *wpt = cache->wpt;
1323 gint id = 2;
1324 while(wpt) {
1325 if(pos_valid(&wpt->pos)) {
1326 GtkWidget *icon = NULL;
1327 if(wpt->sym != WPT_SYM_UNKNOWN)
1328 icon = icon_get_widget(ICON_POS, wpt->sym);
1329
1330 char *name = wpt->desc;
1331 if(!name) name = wpt->cmt;
1332 if(!name) name = wpt->id;
1333
1334 menu_add(menu, appdata, icon, name, cb_cache, id,
1335 lon_entry, lat_entry);
1336 }
1337
1338 id++;
1339 wpt = wpt->next;
1340 }
1341 }
1342
1343 gtk_widget_show_all(menu);
1344
1345 return menu;
1346 }
1347
1348 static gint on_popup_button_press(GtkWidget *button, GdkEventButton *event,
1349 gpointer data) {
1350
1351 appdata_t *appdata = (appdata_t*)data;
1352
1353 if(event->type == GDK_BUTTON_PRESS) {
1354 GtkWidget *menu = g_object_get_data(G_OBJECT(button), "menu");
1355
1356 if(menu)
1357 gtk_widget_destroy(menu);
1358
1359 gpointer lat_entry = g_object_get_data(G_OBJECT(button), "lat_entry");
1360 g_assert(lat_entry);
1361 gpointer lon_entry = g_object_get_data(G_OBJECT(button), "lon_entry");
1362 g_assert(lon_entry);
1363
1364 menu = popup_menu_create(appdata, lat_entry, lon_entry);
1365 g_object_set_data(G_OBJECT(button), "menu", (gpointer)menu);
1366
1367 /* draw a popup menu */
1368 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1369 event->button, event->time);
1370 return TRUE;
1371 }
1372 return FALSE;
1373 }
1374
1375 static void on_popup_destroy(GtkWidget *widget, gpointer user_data ) {
1376 GtkWidget *menu = g_object_get_data(G_OBJECT(widget), "menu");
1377 if(menu) gtk_widget_destroy(menu);
1378 }
1379 #endif
1380
1381 #ifdef PRESET_PICKER_DIALOG
1382
1383 enum {
1384 PRESET_PICKER_COL_ICON = 0,
1385 PRESET_PICKER_COL_NAME,
1386 PRESET_PICKER_COL_ID,
1387 PRESET_PICKER_COL_CB,
1388 PRESET_PICKER_NUM_COLS
1389 };
1390
1391 static void preset_picker_add(GtkListStore *store, appdata_t *appdata,
1392 GdkPixbuf *icon, char *menu_str,
1393 void(*func)(GtkWidget*, gpointer), gint id) {
1394 GtkTreeIter iter;
1395
1396 /* Append a row and fill in some data */
1397 gtk_list_store_append (store, &iter);
1398
1399 gtk_list_store_set(store, &iter,
1400 PRESET_PICKER_COL_ICON, icon,
1401 PRESET_PICKER_COL_NAME, menu_str,
1402 PRESET_PICKER_COL_ID, id,
1403 PRESET_PICKER_COL_CB, func,
1404 -1);
1405 }
1406
1407 static void on_preset_picker_activated(GtkTreeView *treeview,
1408 GtkTreePath *path,
1409 GtkTreeViewColumn *col,
1410 gpointer userdata) {
1411 GtkTreeIter iter;
1412 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
1413
1414 if(gtk_tree_model_get_iter(model, &iter, path)) {
1415 gint id;
1416 void(*func)(GtkWidget*, gpointer);
1417 gtk_tree_model_get(model, &iter,
1418 PRESET_PICKER_COL_ID, &id,
1419 PRESET_PICKER_COL_CB, &func,
1420 -1);
1421
1422 /* set id on widget as callbacks expect it this way */
1423 g_object_set_data(G_OBJECT(treeview), "id", (gpointer)id);
1424 func(GTK_WIDGET(treeview), userdata);
1425
1426 gtk_dialog_response(GTK_DIALOG(gtk_widget_get_toplevel(
1427 GTK_WIDGET(treeview))), GTK_RESPONSE_ACCEPT);
1428
1429 }
1430 }
1431
1432 static GtkWidget *preset_picker_create(appdata_t *appdata,
1433 GtkWidget *lat_entry, GtkWidget *lon_entry) {
1434 GtkCellRenderer *renderer;
1435 GtkListStore *store;
1436
1437 GtkWidget *view = gtk_tree_view_new();
1438
1439 g_object_set_data(G_OBJECT(view), "lat_entry", (gpointer)lat_entry);
1440 g_object_set_data(G_OBJECT(view), "lon_entry", (gpointer)lon_entry);
1441
1442 /* --- "Icon" column --- */
1443 renderer = gtk_cell_renderer_pixbuf_new();
1444 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
1445 -1, "Icon", renderer, "pixbuf", PRESET_PICKER_COL_ICON, NULL);
1446
1447 /* --- "Name" column --- */
1448 renderer = gtk_cell_renderer_text_new();
1449 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
1450 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
1451 "Name", renderer, "text", PRESET_PICKER_COL_NAME, NULL);
1452 gtk_tree_view_column_set_expand(column, TRUE);
1453 gtk_tree_view_insert_column(GTK_TREE_VIEW(view), column, -1);
1454
1455 store = gtk_list_store_new(PRESET_PICKER_NUM_COLS,
1456 GDK_TYPE_PIXBUF,
1457 G_TYPE_STRING,
1458 G_TYPE_INT,
1459 G_TYPE_POINTER);
1460
1461 if(pos_valid(gps_get_pos(appdata)))
1462 preset_picker_add(store, appdata, icon_get(ICON_POS, 18),
1463 _("GPS position"), cb_gps, 0);
1464
1465 if(pos_valid(&appdata->home))
1466 preset_picker_add(store, appdata, icon_get(ICON_POS, 21),
1467 _("Home"), cb_gps, 1);
1468
1469 location_t *location = appdata->location;
1470 gint id = 2;
1471 while(location) {
1472 if(pos_valid(&location->pos))
1473 preset_picker_add(store, appdata, icon_get(ICON_POS, 17),
1474 location->name, cb_gps, id);
1475
1476 id++;
1477 location = location->next;
1478 }
1479
1480 if(pos_valid(&appdata->geomath))
1481 preset_picker_add(store, appdata, icon_get(ICON_POS, 19),
1482 _("Geomath projection"), cb_geomath, 0);
1483
1484 #ifdef ENABLE_OSM_GPS_MAP
1485 if(pos_valid(&appdata->map.pos))
1486 preset_picker_add(store, appdata, icon_get(ICON_POS, 20),
1487 _("Map position"), cb_map, 0);
1488 #endif
1489
1490 #ifdef ENABLE_MAEMO_MAPPER
1491 if(appdata->mmpos_valid) {
1492 preset_picker_add(store, appdata, icon_get(ICON_POS, 24),
1493 _("Maemo Mapper position"), cb_mm, 0);
1494 }
1495 #endif
1496
1497 if(appdata->cur_cache) {
1498 cache_t *cache = appdata->cur_cache;
1499
1500 char *name = cache->name;
1501 if(!name) name = cache->id;
1502
1503 /* original cache position */
1504 if(pos_valid(&cache->pos))
1505 preset_picker_add(store, appdata, icon_get(ICON_POS, cache->type + 6),
1506 name, cb_cache, 0);
1507
1508 /* overwritten cache position */
1509 if(appdata->cache_context && notes_get_override(appdata->cache_context))
1510 preset_picker_add(store, appdata, icon_get(ICON_POS, cache->type + 6),
1511 _("Modified coordinate"), cb_cache, 1);
1512
1513 wpt_t *wpt = cache->wpt;
1514 gint id = 2;
1515 while(wpt) {
1516 if(pos_valid(&wpt->pos)) {
1517 GdkPixbuf *icon = NULL;
1518 if(wpt->sym != WPT_SYM_UNKNOWN)
1519 icon = icon_get(ICON_POS, wpt->sym);
1520
1521 char *name = wpt->desc;
1522 if(!name) name = wpt->cmt;
1523 if(!name) name = wpt->id;
1524
1525 preset_picker_add(store, appdata, icon, name, cb_cache, id);
1526 }
1527 id++;
1528 wpt = wpt->next;
1529 }
1530 }
1531
1532 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
1533 g_object_unref(store);
1534
1535 /* make list react on clicks */
1536 g_signal_connect(view, "row-activated",
1537 (GCallback)on_preset_picker_activated, appdata);
1538
1539 /* put this inside a scrolled view */
1540 #ifndef USE_PANNABLE_AREA
1541 GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1542 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1543 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1544 gtk_container_add(GTK_CONTAINER(scrolled_window), view);
1545 return scrolled_window;
1546 #else
1547 GtkWidget *pannable_area = hildon_pannable_area_new();
1548 gtk_container_add(GTK_CONTAINER(pannable_area), view);
1549 return pannable_area;
1550 #endif
1551 }
1552
1553 static gint on_preset_picker_button_press(GtkWidget *button,
1554 GdkEventButton *event, gpointer data) {
1555 appdata_t *appdata = (appdata_t*)data;
1556
1557 gpointer lat_entry = g_object_get_data(G_OBJECT(button), "lat_entry");
1558 gpointer lon_entry = g_object_get_data(G_OBJECT(button), "lon_entry");
1559
1560 if(event->type == GDK_BUTTON_PRESS) {
1561 GtkWidget *dialog =
1562 gtk_dialog_new_with_buttons(_("Use coordinate"),
1563 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
1564 GTK_DIALOG_MODAL,
1565 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1566 NULL);
1567
1568 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 210);
1569
1570 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1571 preset_picker_create(appdata, lat_entry, lon_entry));
1572
1573 gtk_widget_show_all(dialog);
1574 gtk_dialog_run(GTK_DIALOG(dialog));
1575 gtk_widget_destroy(dialog);
1576
1577 return TRUE;
1578 }
1579 return FALSE;
1580 }
1581 #endif
1582
1583
1584 GtkWidget *preset_coordinate_picker(appdata_t *appdata,
1585 GtkWidget *lat_entry, GtkWidget *lon_entry) {
1586
1587 GtkWidget *button = gtk_button_new();
1588
1589 gtk_button_set_image(GTK_BUTTON(button), icon_get_widget(ICON_POS, 22));
1590
1591 #ifndef USE_MAEMO
1592 gtk_widget_set_tooltip_text(button, _("Use coordinate"));
1593 #endif
1594
1595 g_object_set_data(G_OBJECT(button), "lat_entry", (gpointer)lat_entry);
1596 g_object_set_data(G_OBJECT(button), "lon_entry", (gpointer)lon_entry);
1597
1598 #ifndef PRESET_PICKER_DIALOG
1599 gtk_signal_connect(GTK_OBJECT(button), "button-press-event",
1600 (GtkSignalFunc)on_popup_button_press, appdata);
1601
1602 gtk_signal_connect(GTK_OBJECT(button), "destroy",
1603 (GtkSignalFunc)on_popup_destroy, appdata);
1604 #else
1605 #ifdef FREMANTLE
1606 hildon_gtk_widget_set_theme_size(button,
1607 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1608 #endif
1609
1610 gtk_signal_connect(GTK_OBJECT(button), "button-press-event",
1611 (GtkSignalFunc)on_preset_picker_button_press, appdata);
1612 #endif
1613
1614 return button;
1615 }
1616
1617 #if defined(ENABLE_MAEMO_MAPPER) || defined(ENABLE_OSM_GPS_MAP)
1618 static pos_t goto_pos_get(GtkWidget *item) {
1619 pos_t pos;
1620
1621 GtkWidget *lat_entry = g_object_get_data(G_OBJECT(item), "lat_entry");
1622 pos.lat = lat_entry_get(lat_entry);
1623
1624 GtkWidget *lon_entry = g_object_get_data(G_OBJECT(item), "lon_entry");
1625 pos.lon = lon_entry_get(lon_entry);
1626
1627 return pos;
1628 }
1629
1630 #if defined(ENABLE_MAEMO_MAPPER) && defined(ENABLE_OSM_GPS_MAP)
1631 #ifdef ENABLE_MAEMO_MAPPER
1632 static void cb_mm_set(GtkWidget *item, gpointer data) {
1633 appdata_t *appdata = (appdata_t*)data;
1634
1635 pos_t pos = goto_pos_get(item);
1636 if(!isnan(pos.lat) && !isnan(pos.lon))
1637 dbus_mm_set_position(appdata, &pos);
1638 }
1639 #endif
1640
1641 #ifdef ENABLE_OSM_GPS_MAP
1642 static void cb_map_set(GtkWidget *item, gpointer data) {
1643 appdata_t *appdata = (appdata_t*)data;
1644
1645 pos_t pos = goto_pos_get(item);
1646 if(!isnan(pos.lat) && !isnan(pos.lon)) {
1647 map(appdata);
1648 osm_gps_map_set_center(OSM_GPS_MAP(appdata->map.context->widget),
1649 pos.lat, pos.lon);
1650 }
1651 }
1652 #endif
1653
1654 #ifndef PRESET_PICKER_DIALOG
1655 static GtkWidget *goto_popup_menu_create(appdata_t *appdata,
1656 GtkWidget *lat_entry, GtkWidget *lon_entry) {
1657 GtkWidget *menu = gtk_menu_new();
1658
1659 #if defined(ENABLE_OSM_GPS_MAP)
1660 menu_add(menu, appdata, icon_get_widget(ICON_POS, 20),
1661 _("Internal map"), cb_map_set, 0, lon_entry, lat_entry);
1662 #endif
1663
1664 #if defined(ENABLE_MAEMO_MAPPER)
1665 menu_add(menu, appdata, icon_get_widget(ICON_POS, 24),
1666 _("Maemo Mapper"), cb_mm_set, 0, lon_entry, lat_entry);
1667 #endif
1668
1669 gtk_widget_show_all(menu);
1670
1671 return menu;
1672 }
1673 #else
1674 static GtkWidget *goto_picker_create(appdata_t *appdata,
1675 GtkWidget *lat_entry, GtkWidget *lon_entry) {
1676 GtkCellRenderer *renderer;
1677 GtkListStore *store;
1678
1679 GtkWidget *view = gtk_tree_view_new();
1680
1681 g_object_set_data(G_OBJECT(view), "lat_entry", (gpointer)lat_entry);
1682 g_object_set_data(G_OBJECT(view), "lon_entry", (gpointer)lon_entry);
1683
1684 /* --- "Icon" column --- */
1685 renderer = gtk_cell_renderer_pixbuf_new();
1686 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
1687 -1, "Icon", renderer, "pixbuf", PRESET_PICKER_COL_ICON, NULL);
1688
1689 /* --- "Name" column --- */
1690 renderer = gtk_cell_renderer_text_new();
1691 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
1692 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
1693 "Name", renderer, "text", PRESET_PICKER_COL_NAME, NULL);
1694 gtk_tree_view_column_set_expand(column, TRUE);
1695 gtk_tree_view_insert_column(GTK_TREE_VIEW(view), column, -1);
1696
1697 store = gtk_list_store_new(PRESET_PICKER_NUM_COLS,
1698 GDK_TYPE_PIXBUF,
1699 G_TYPE_STRING,
1700 G_TYPE_INT,
1701 G_TYPE_POINTER);
1702
1703 #if defined(ENABLE_OSM_GPS_MAP)
1704 preset_picker_add(store, appdata, icon_get(ICON_POS, 20),
1705 _("Internal map"), cb_map_set, 0);
1706 #endif
1707
1708 #if defined(ENABLE_MAEMO_MAPPER)
1709 preset_picker_add(store, appdata, icon_get(ICON_POS, 24),
1710 _("Maemo Mapper"), cb_mm_set, 0);
1711 #endif
1712
1713 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
1714 g_object_unref(store);
1715
1716 /* make list react on clicks */
1717 g_signal_connect(view, "row-activated",
1718 (GCallback)on_preset_picker_activated, appdata);
1719
1720 /* put this inside a scrolled view */
1721 #ifndef USE_PANNABLE_AREA
1722 GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1723 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1724 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1725 gtk_container_add(GTK_CONTAINER(scrolled_window), view);
1726 return scrolled_window;
1727 #else
1728 GtkWidget *pannable_area = hildon_pannable_area_new();
1729 gtk_container_add(GTK_CONTAINER(pannable_area), view);
1730 return pannable_area;
1731 #endif
1732 }
1733 #endif
1734 #endif
1735
1736 static gint on_goto_button_press(GtkWidget *button,
1737 GdkEventButton *event, gpointer data) {
1738 appdata_t *appdata = (appdata_t*)data;
1739
1740 if(event->type == GDK_BUTTON_PRESS) {
1741
1742 #if defined(ENABLE_MAEMO_MAPPER) && !defined(ENABLE_OSM_GPS_MAP)
1743 /* only maemo mapper is being used */
1744 pos_t pos = goto_pos_get(button);
1745 if(!isnan(pos.lat) && !isnan(pos.lon))
1746 dbus_mm_set_position(appdata, &pos);
1747 #elif !defined(ENABLE_MAEMO_MAPPER) && defined(ENABLE_OSM_GPS_MAP)
1748 /* only internal map is being used */
1749 pos_t pos = goto_pos_get(button);
1750 if(!isnan(pos.lat) && !isnan(pos.lon)) {
1751 map(appdata);
1752 osm_gps_map_set_center(OSM_GPS_MAP(appdata->map.context->widget),
1753 pos.lat, pos.lon);
1754 }
1755 #else
1756
1757 gpointer lat_entry = g_object_get_data(G_OBJECT(button), "lat_entry");
1758 g_assert(lat_entry);
1759 gpointer lon_entry = g_object_get_data(G_OBJECT(button), "lon_entry");
1760 g_assert(lon_entry);
1761
1762 #ifndef PRESET_PICKER_DIALOG
1763 GtkWidget *menu = g_object_get_data(G_OBJECT(button), "menu");
1764 if(!menu) {
1765 menu = goto_popup_menu_create(appdata, lat_entry, lon_entry);
1766 g_object_set_data(G_OBJECT(button), "menu", (gpointer)menu);
1767 }
1768
1769 /* draw a popup menu */
1770 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1771 event->button, event->time);
1772 #else
1773 GtkWidget *dialog =
1774 gtk_dialog_new_with_buttons(_("Show coordinate on map"),
1775 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
1776 GTK_DIALOG_MODAL,
1777 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1778 NULL);
1779
1780 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 210);
1781
1782 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1783 goto_picker_create(appdata, lat_entry, lon_entry));
1784
1785 gtk_widget_show_all(dialog);
1786 gtk_dialog_run(GTK_DIALOG(dialog));
1787 gtk_widget_destroy(dialog);
1788 #endif
1789
1790 #endif
1791
1792 return TRUE;
1793 }
1794 return FALSE;
1795 }
1796
1797 /* a button which makes the map/maemo mapper go to the associated */
1798 /* position */
1799 GtkWidget *goto_coordinate(appdata_t *appdata,
1800 GtkWidget *lat_entry, GtkWidget *lon_entry) {
1801
1802 GtkWidget *button = gtk_button_new();
1803
1804 gtk_button_set_image(GTK_BUTTON(button), icon_get_widget(ICON_POS, 23));
1805
1806 #ifndef USE_MAEMO
1807 gtk_widget_set_tooltip_text(button, _("Show coordinate on map"));
1808 #endif
1809
1810 g_object_set_data(G_OBJECT(button), "lat_entry", (gpointer)lat_entry);
1811 g_object_set_data(G_OBJECT(button), "lon_entry", (gpointer)lon_entry);
1812
1813 gtk_signal_connect(GTK_OBJECT(button), "button-press-event",
1814 (GtkSignalFunc)on_goto_button_press, appdata);
1815
1816 #ifndef PRESET_PICKER_DIALOG
1817 gtk_signal_connect(GTK_OBJECT(button), "destroy",
1818 (GtkSignalFunc)on_popup_destroy, appdata);
1819 #else
1820 #ifdef FREMANTLE
1821 hildon_gtk_widget_set_theme_size(button,
1822 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1823 #endif
1824 #endif
1825
1826 return button;
1827 }
1828 #else
1829 /* no map installed */
1830 GtkWidget *goto_coordinate(appdata_t *appdata,
1831 GtkWidget *lat_entry, GtkWidget *lon_entry) {
1832 return gtk_label_new("");
1833 }
1834 #endif
1835
1836 GtkWidget *entry_new(void) {
1837 #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
1838 return gtk_entry_new();
1839 #else
1840 return hildon_entry_new(HILDON_SIZE_AUTO);
1841 #endif
1842 }
1843
1844 gboolean pos_differ(pos_t *pos1, pos_t *pos2) {
1845 int lat1 = (60000 * pos1->lat)+0.5, lon1 = (60000 * pos1->lon)+0.5;
1846 int lat2 = (60000 * pos2->lat)+0.5, lon2 = (60000 * pos2->lon)+0.5;
1847
1848 return((lat1 != lat2) || (lon1 != lon2));
1849 }
1850
1851 gboolean pos_valid(pos_t *pos) {
1852 if(!pos) return FALSE;
1853 return(!isnan(pos->lat) && !isnan(pos->lon));
1854 }
1855
1856 void misc_init(void) {
1857 g_signal_new ("changed", GTK_TYPE_BUTTON,
1858 G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
1859 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1860 }
1861
1862 void angle_str(char *str, int len, float angle) {
1863 snprintf(str, len, _("%.1f°"), angle);
1864 }
1865
1866 float angle_parse(char *str) {
1867 float val;
1868
1869 if(sscanf(str, _("%f°"), &val) != 1)
1870 val = NAN;
1871
1872 return val;
1873 }
1874
1875 #ifndef COORDINATE_PICKER
1876 static void callback_modified_angle(GtkWidget *widget, gpointer data ) {
1877 float i = angle_parse((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
1878 mark(widget, !isnan(i));
1879 }
1880 #else
1881 static gint on_angle_picker_button_press(GtkWidget *button,
1882 GdkEventButton *event, gpointer data) {
1883
1884 if(event->type == GDK_BUTTON_PRESS) {
1885 GtkWidget *dialog =
1886 gtk_dialog_new_with_buttons(_("Direction"),
1887 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
1888 GTK_DIALOG_MODAL,
1889 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1890 _("Done"), GTK_RESPONSE_ACCEPT,
1891 NULL);
1892
1893 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 210);
1894
1895 int i, angle = (int)g_object_get_data(G_OBJECT(button), "angle");
1896
1897 GtkWidget *anglew[3], *fracw;
1898
1899 /* create xxx.x° */
1900 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
1901
1902 gtk_box_pack_start_defaults(GTK_BOX(hbox),
1903 anglew[0] = digit_picker_create(0,3, (angle/1000)%10));
1904 gtk_box_pack_start_defaults(GTK_BOX(hbox),
1905 anglew[1] = digit_picker_create(0,9, (angle/100)%10));
1906 gtk_box_pack_start_defaults(GTK_BOX(hbox),
1907 anglew[2] = digit_picker_create(0,9, (angle/10)%10));
1908 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" . "), FALSE, FALSE, 0);
1909 gtk_box_pack_start_defaults(GTK_BOX(hbox),
1910 fracw = digit_picker_create(0,9, (angle/1)%10));
1911 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("°"), FALSE, FALSE, 0);
1912
1913 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
1914
1915 gtk_widget_show_all(dialog);
1916 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1917
1918 /* parse angle */
1919 for(angle=0,i=0;i<3;i++)
1920 angle = 10 * angle + picker_get(anglew[i]);
1921
1922 angle = 10 * angle + picker_get(fracw);
1923
1924 angle_entry_set(button, angle/10.0);
1925 }
1926
1927 gtk_widget_destroy(dialog);
1928
1929 return TRUE;
1930 }
1931 return FALSE;
1932 }
1933 #endif
1934
1935 GtkWidget *angle_entry_new(float angle) {
1936 char str[32];
1937 angle_str(str, sizeof(str), angle);
1938
1939 #ifndef COORDINATE_PICKER
1940 GtkWidget *widget = red_entry_new_with_text(str);
1941 g_signal_connect(G_OBJECT(widget), "changed",
1942 G_CALLBACK(callback_modified_angle), NULL);
1943 #else
1944 GtkWidget *widget = gtk_button_new_with_label(str);
1945
1946 #ifdef FREMANTLE
1947 hildon_gtk_widget_set_theme_size(widget,
1948 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1949 #endif
1950 int angle_int = (int)roundf(angle*10.0);
1951 g_object_set_data(G_OBJECT(widget), "angle", (gpointer)angle_int);
1952 gtk_signal_connect(GTK_OBJECT(widget), "button-press-event",
1953 (GtkSignalFunc)on_angle_picker_button_press, NULL);
1954 #endif
1955 return widget;
1956 }
1957
1958 float angle_entry_get(GtkWidget *widget) {
1959 #ifndef COORDINATE_PICKER
1960 char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
1961 #else
1962 char *p = (char*)gtk_button_get_label(GTK_BUTTON(widget));
1963 #endif
1964 return angle_parse(p);
1965 }
1966
1967 void angle_entry_set(GtkWidget *widget, float angle) {
1968 char str[32];
1969 angle_str(str, sizeof(str)-1, angle);
1970 #ifndef COORDINATE_PICKER
1971 gtk_entry_set_text(GTK_ENTRY(widget), str);
1972 #else
1973 gtk_button_set_label(GTK_BUTTON(widget), str);
1974 int angle_int = (int)roundf(angle * 10.0);
1975 g_object_set_data(G_OBJECT(widget), "angle", (gpointer)angle_int);
1976 g_signal_emit_by_name(widget, "changed");
1977 #endif
1978 }
1979
1980 GtkWidget *toggle_button_new_with_label(char *label) {
1981 #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
1982 return gtk_check_button_new_with_label(label);
1983 #else
1984 GtkWidget *cbut = gtk_toggle_button_new_with_label(label);
1985 hildon_gtk_widget_set_theme_size(cbut,
1986 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1987 gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(cbut), FALSE);
1988 return cbut;
1989 #endif
1990 }
1991
1992 void toggle_button_set_active(GtkWidget *button, gboolean active) {
1993 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), active);
1994 }
1995
1996 gboolean toggle_button_get_active(GtkWidget *button) {
1997 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1998 }
1999
2000 GtkWidget *check_button_new_with_label(char *label) {
2001 #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
2002 return gtk_check_button_new_with_label(label);
2003 #else
2004 GtkWidget *cbut =
2005 hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT |
2006 HILDON_SIZE_AUTO_WIDTH);
2007 gtk_button_set_label(GTK_BUTTON(cbut), label);
2008 return cbut;
2009 #endif
2010 }
2011
2012 void check_button_set_active(GtkWidget *button, gboolean active) {
2013 #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
2014 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), active);
2015 #else
2016 hildon_check_button_set_active(HILDON_CHECK_BUTTON(button), active);
2017 #endif
2018 }
2019
2020 gboolean check_button_get_active(GtkWidget *button) {
2021 #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
2022 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
2023 #else
2024 return hildon_check_button_get_active(HILDON_CHECK_BUTTON(button));
2025 #endif
2026 }
2027
2028 GtkWidget *button_new(void) {
2029 GtkWidget *button = gtk_button_new();
2030 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
2031 hildon_gtk_widget_set_theme_size(button,
2032 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
2033 #endif
2034 return button;
2035 }
2036
2037 GtkWidget *button_new_with_label(char *label) {
2038 GtkWidget *button = gtk_button_new_with_label(label);
2039 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
2040 hildon_gtk_widget_set_theme_size(button,
2041 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
2042 #endif
2043 return button;
2044 }
2045
2046 static void on_browse(GtkWidget *widget, gpointer data) {
2047 GtkWidget *dialog;
2048
2049 char **fileptr = g_object_get_data(G_OBJECT(widget), "fileptr");
2050
2051 #ifdef USE_MAEMO
2052 dialog = hildon_file_chooser_dialog_new(
2053 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(widget))),
2054 GTK_FILE_CHOOSER_ACTION_SAVE);
2055 #else
2056 dialog = gtk_file_chooser_dialog_new(
2057 g_object_get_data(G_OBJECT(widget), "sel_title"),
2058 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(widget))),
2059 GTK_FILE_CHOOSER_ACTION_SAVE,
2060 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2061 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2062 NULL);
2063 #endif
2064
2065 if(*fileptr) {
2066 printf("set filename <%s>\n", *fileptr);
2067
2068 if(!g_file_test(*fileptr, G_FILE_TEST_EXISTS)) {
2069 char *last_sep = strrchr(*fileptr, '/');
2070 if(last_sep) {
2071 *last_sep = 0; // seperate path from file
2072
2073 /* the user just created a new document */
2074 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
2075 *fileptr);
2076 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
2077 last_sep+1);
2078
2079 /* restore full filename */
2080 *last_sep = '/';
2081 }
2082 } else
2083 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), *fileptr);
2084 }
2085
2086 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_FM_OK) {
2087 gchar *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2088 if(name) {
2089 if(*fileptr) g_free(*fileptr);
2090 *fileptr = g_strdup(name);
2091
2092 #ifndef FREMANTLE
2093 GtkWidget *label = g_object_get_data(G_OBJECT(widget), "label");
2094 gtk_label_set_text(GTK_LABEL(label), *fileptr);
2095 #else
2096 hildon_button_set_value(HILDON_BUTTON(widget), *fileptr);
2097 #endif
2098 }
2099 }
2100
2101 gtk_widget_destroy (dialog);
2102 }
2103
2104 GtkWidget *export_file(char *sel_title, char **filename) {
2105 /* non-fremantle and classic gtk use a rather ugly and */
2106 /* complex layout */
2107 #ifndef FREMANTLE
2108 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
2109 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
2110 GtkWidget *label = gtk_label_new(_("Export to"));
2111 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE,0);
2112 gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
2113 GtkWidget *button = button_new_with_label(_("Browse"));
2114 g_object_set_data(G_OBJECT(button), "sel_title", sel_title);
2115 g_object_set_data(G_OBJECT(button), "fileptr", filename);
2116 gtk_signal_connect(GTK_OBJECT(button), "clicked",
2117 GTK_SIGNAL_FUNC(on_browse), NULL);
2118 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE,0);
2119 gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);
2120
2121 label = gtk_label_new((*filename)?*filename:"");
2122 g_object_set_data(G_OBJECT(button), "label", label);
2123 gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
2124 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
2125
2126 gtk_box_pack_start_defaults(GTK_BOX(vbox), label);
2127 return vbox;
2128 #else
2129 GtkWidget *button = hildon_button_new_with_text(
2130 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH),
2131 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
2132 _("Export to"), *filename);
2133 hildon_button_set_title_alignment(HILDON_BUTTON(button), 0.5, 0.5);
2134 hildon_button_set_value_alignment(HILDON_BUTTON(button), 0.5, 0.5);
2135 g_object_set_data(G_OBJECT(button), "sel_title", sel_title);
2136 g_object_set_data(G_OBJECT(button), "fileptr", filename);
2137 gtk_signal_connect(GTK_OBJECT(button), "clicked",
2138 GTK_SIGNAL_FUNC(on_browse), NULL);
2139 return button;
2140 #endif
2141 }
2142
2143