Contents of /trunk/src/misc.c

Parent Directory Parent Directory | Revision Log Revision Log


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