Contents of /trunk/src/misc.c

Parent Directory Parent Directory | Revision Log Revision Log


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