Contents of /trunk/src/misc.c

Parent Directory Parent Directory | Revision Log Revision Log


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