Diff of /trunk/src/misc.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 34 by harbaum, Wed Jul 29 19:24:15 2009 UTC revision 226 by harbaum, Wed Dec 2 20:05:52 2009 UTC
# Line 17  Line 17 
17   * along with GPXView.  If not, see <http://www.gnu.org/licenses/>.   * along with GPXView.  If not, see <http://www.gnu.org/licenses/>.
18   */   */
19    
20    /* TODO:
21    */
22    
23  #include <math.h>  #include <math.h>
24  #include <string.h>  #include <string.h>
25  #include <ctype.h>  #include <ctype.h>
26    
27    #include <glib.h>
28    #include <glib/gstdio.h>
29    
30  #include "gpxview.h"  #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) {  char strlastchr(char *str) {
50    return str[strlen(str)]-1;    return str[strlen(str)]-1;
51  }  }
# Line 148  float pos_parse_lon(char *str) { Line 171  float pos_parse_lon(char *str) {
171    
172  const char *pos_get_bearing_str(pos_t from, pos_t to) {  const char *pos_get_bearing_str(pos_t from, pos_t to) {
173    static const char *bear_str[]={    static const char *bear_str[]={
174      "N", "NE", "E", "SE", "S", "SW", "W", "NW" };      "N", "NE", "E", "SE", "S", "SW", "W", "NW", "" };
175    int idx = (gpx_pos_get_bearing(from, to)+22.5)/45.0;  
176    /* make sure we stay in icon bounds */    float bearing = gpx_pos_get_bearing(from, to);
177    while(idx < 0) idx += 8;    if(!isnan(bearing)) {
178    while(idx > 7) idx -= 8;      int idx = (bearing+22.5)/45.0;
179    return _(bear_str[idx]);      /* 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 */  /* the maemo font size is quite huge, so we adjust some fonts */
# Line 284  float distance_parse(char *str, gboolean Line 313  float distance_parse(char *str, gboolean
313    return val;    return val;
314  }  }
315    
316    /* ------------------ coordinate picker tool --------------------------- */
317    
318    #ifndef COORDINATE_PICKER
319  static gboolean mark(GtkWidget *widget, gboolean valid) {  static gboolean mark(GtkWidget *widget, gboolean valid) {
320    gtk_widget_set_state(widget, valid?GTK_STATE_NORMAL:TAG_STATE);    gtk_widget_set_state(widget, valid?GTK_STATE_NORMAL:TAG_STATE);
321    return valid;    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 ) {  static void callback_modified_lat(GtkWidget *widget, gpointer data ) {
336    float i = pos_parse_lat((char*)gtk_entry_get_text(GTK_ENTRY(widget)));    float i = pos_parse_lat((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
337    mark(widget, !isnan(i));    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" */  /* a entry that is colored red when being "active" */
611  GtkWidget *lat_entry_new(float lat) {  GtkWidget *lat_entry_new(float lat) {
   GdkColor color;  
   GtkWidget *widget = gtk_entry_new();  
   gdk_color_parse("#ff0000", &color);  
   gtk_widget_modify_text(widget, TAG_STATE, &color);  
   
612    char str[32];    char str[32];
613    pos_lat_str(str, sizeof(str), lat);    pos_lat_str(str, sizeof(str), lat);
614    gtk_entry_set_text(GTK_ENTRY(widget), str);  
615    #ifndef COORDINATE_PICKER
616      GtkWidget *widget = red_entry_new_with_text(str);
617    
618    g_signal_connect(G_OBJECT(widget), "changed",    g_signal_connect(G_OBJECT(widget), "changed",
619                     G_CALLBACK(callback_modified_lat), NULL);                     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;    return widget;
635  }  }
636    
637    #ifndef COORDINATE_PICKER
638  static void callback_modified_lon(GtkWidget *widget, gpointer data ) {  static void callback_modified_lon(GtkWidget *widget, gpointer data ) {
639    float i = pos_parse_lon((char*)gtk_entry_get_text(GTK_ENTRY(widget)));    float i = pos_parse_lon((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
640    mark(widget, !isnan(i));    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 */  /* a entry that is colored red when filled with invalid coordinate */
730  GtkWidget *lon_entry_new(float lon) {  GtkWidget *lon_entry_new(float lon) {
   GdkColor color;  
   GtkWidget *widget = gtk_entry_new();  
   gdk_color_parse("#ff0000", &color);  
   gtk_widget_modify_text(widget, TAG_STATE, &color);  
   
731    char str[32];    char str[32];
732    pos_lon_str(str, sizeof(str), lon);    pos_lon_str(str, sizeof(str), lon);
   gtk_entry_set_text(GTK_ENTRY(widget), str);  
733    
734    #ifndef COORDINATE_PICKER
735      GtkWidget *widget = red_entry_new_with_text(str);
736    g_signal_connect(G_OBJECT(widget), "changed",    g_signal_connect(G_OBJECT(widget), "changed",
737                     G_CALLBACK(callback_modified_lon), NULL);                     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;    return widget;
753  }  }
754    
755    float lat_entry_get(GtkWidget *widget) {
756  float lat_get(GtkWidget *widget) {  #ifndef COORDINATE_PICKER
757    char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));    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);    return pos_parse_lat(p);
762  }  }
763    
764  float lon_get(GtkWidget *widget) {  float lon_entry_get(GtkWidget *widget) {
765    #ifndef COORDINATE_PICKER
766    char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));    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);    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 ) {  static void callback_modified_dist(GtkWidget *widget, gpointer data ) {
827    /* don't care for metric/imperial since we only want to know if this */    /* don't care for metric/imperial since we only want to know if this */
828    /* is parseable at all */    /* is parseable at all */
829    float i = distance_parse((char*)gtk_entry_get_text(GTK_ENTRY(widget)), FALSE);    float i =
830        distance_parse((char*)gtk_entry_get_text(GTK_ENTRY(widget)), FALSE);
831    mark(widget, !isnan(i));    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 */  /* a entry that is colored red when filled with invalid distance */
928  GtkWidget *dist_entry_new(float dist, gboolean mil) {  GtkWidget *dist_entry_new(float dist, gboolean mil) {
   GdkColor color;  
   GtkWidget *widget = gtk_entry_new();  
   gdk_color_parse("#ff0000", &color);  
   gtk_widget_modify_text(widget, TAG_STATE, &color);  
   
929    char str[32];    char str[32];
930    distance_str(str, sizeof(str), dist, mil);    distance_str(str, sizeof(str), dist, mil);
   gtk_entry_set_text(GTK_ENTRY(widget), str);  
931    
932    #ifndef COORDINATE_PICKER
933      GtkWidget *widget = red_entry_new_with_text(str);
934    g_signal_connect(G_OBJECT(widget), "changed",    g_signal_connect(G_OBJECT(widget), "changed",
935                     G_CALLBACK(callback_modified_dist), NULL);                     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;    return widget;
954  }  }
955    
956  float dist_get(GtkWidget *widget, gboolean mil) {  float dist_entry_get(GtkWidget *widget, gboolean mil) {
957    #ifndef COORDINATE_PICKER
958    char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));    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);    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  #ifndef USE_MAEMO
981  #ifdef ENABLE_BROWSER_INTERFACE  #ifdef ENABLE_BROWSER_INTERFACE
982  #include <libgnome/gnome-url.h>  #include <libgnome/gnome-url.h>
# Line 385  int browser_url(appdata_t *appdata, char Line 989  int browser_url(appdata_t *appdata, char
989  }  }
990  #endif  #endif
991  #endif  #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        pos_set(item, cache->notes->pos.lat, cache->notes->pos.lon);
1183      else {
1184        wpt_t *wpt = cache->wpt;
1185        while(wpt && id > 2) {
1186          wpt = wpt->next;
1187          id--;
1188        }
1189    
1190        if(id == 2)
1191          pos_set(item, wpt->pos.lat, wpt->pos.lon);
1192      }
1193    }
1194    
1195    #ifndef PRESET_PICKER_DIALOG
1196    static GtkWidget *menu_add(GtkWidget *menu, appdata_t *appdata,
1197                               GtkWidget *icon, char *menu_str,
1198                               void(*func)(GtkWidget*, gpointer), gint id,
1199                               GtkWidget *lon_entry, GtkWidget *lat_entry) {
1200    
1201      GtkWidget *item = gtk_image_menu_item_new_with_label(menu_str);
1202    
1203      if(icon)
1204        gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), icon);
1205    
1206      g_object_set_data(G_OBJECT(item), "lat_entry", (gpointer)lat_entry);
1207      g_object_set_data(G_OBJECT(item), "lon_entry", (gpointer)lon_entry);
1208      g_object_set_data(G_OBJECT(item), "id", (gpointer)id);
1209    
1210      if(func)
1211        gtk_signal_connect(GTK_OBJECT(item), "activate",
1212                           (GtkSignalFunc)func, appdata);
1213    
1214      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1215    
1216      return item;
1217    }
1218    
1219    static GtkWidget *popup_menu_create(appdata_t *appdata,
1220                        GtkWidget *lat_entry, GtkWidget *lon_entry) {
1221      GtkWidget *menu = gtk_menu_new();
1222    
1223      if(pos_valid(gps_get_pos(appdata)))
1224        menu_add(menu, appdata, icon_get_widget(ICON_POS, 18),
1225                 _("GPS position"), cb_gps, 0, lon_entry, lat_entry);
1226    
1227      if(pos_valid(&appdata->home))
1228        menu_add(menu, appdata, icon_get_widget(ICON_POS, 21),
1229                 _("Home"), cb_gps, 1, lon_entry, lat_entry);
1230    
1231      location_t *location = appdata->location;
1232      gint id = 2;
1233      while(location) {
1234        if(pos_valid(&location->pos))
1235          menu_add(menu, appdata, icon_get_widget(ICON_POS, 21),
1236                   location->name, cb_gps, id, lon_entry, lat_entry);
1237    
1238        id++;
1239        location = location->next;
1240      }
1241    
1242      if(pos_valid(&appdata->geomath))
1243        menu_add(menu, appdata, icon_get_widget(ICON_POS, 19),
1244                 _("Geomath projection"), cb_geomath, 0, lon_entry, lat_entry);
1245    
1246    #ifdef ENABLE_OSM_GPS_MAP
1247      if(pos_valid(&appdata->map.pos))
1248        menu_add(menu, appdata, icon_get_widget(ICON_POS, 20), _("Map position"),
1249                 cb_map, 0, lon_entry, lat_entry);
1250    #endif
1251    
1252      if(appdata->cur_cache) {
1253        cache_t *cache = appdata->cur_cache;
1254    
1255        char *name = cache->name;
1256        if(!name) name = cache->id;
1257    
1258        /* original cache position */
1259        if(pos_valid(&cache->pos))
1260          menu_add(menu, appdata, icon_get_widget(ICON_POS, cache->type + 6),
1261                   name, cb_cache, 0, lon_entry, lat_entry);
1262    
1263        /* overwritten cache position */
1264        if(cache->notes && pos_valid(&cache->notes->pos))
1265          menu_add(menu, appdata, icon_get_widget(ICON_POS, cache->type + 6),
1266                   _("Modified coordinate"), cb_cache, 1, lon_entry, lat_entry);
1267    
1268        wpt_t *wpt = cache->wpt;
1269        gint id = 2;
1270        while(wpt) {
1271          if(pos_valid(&wpt->pos)) {
1272            GtkWidget *icon = NULL;
1273            if(wpt->sym != WPT_SYM_UNKNOWN)
1274              icon = icon_get_widget(ICON_POS, wpt->sym);
1275    
1276            char *name = wpt->desc;
1277            if(!name) name = wpt->cmt;
1278            if(!name) name = wpt->id;
1279    
1280            menu_add(menu, appdata, icon, name, cb_cache, id,
1281                     lon_entry, lat_entry);
1282          }
1283    
1284          id++;
1285          wpt = wpt->next;
1286        }
1287      }
1288    
1289      gtk_widget_show_all(menu);
1290    
1291      return menu;
1292    }
1293    
1294    static gint on_popup_button_press(GtkWidget *button, GdkEventButton *event,
1295                                      gpointer data) {
1296    
1297      appdata_t *appdata = (appdata_t*)data;
1298    
1299      if(event->type == GDK_BUTTON_PRESS) {
1300        GtkWidget *menu = g_object_get_data(G_OBJECT(button), "menu");
1301    
1302        if(menu)
1303          gtk_widget_destroy(menu);
1304    
1305        gpointer lat_entry = g_object_get_data(G_OBJECT(button), "lat_entry");
1306        g_assert(lat_entry);
1307        gpointer lon_entry = g_object_get_data(G_OBJECT(button), "lon_entry");
1308        g_assert(lon_entry);
1309    
1310        menu = popup_menu_create(appdata, lat_entry, lon_entry);
1311        g_object_set_data(G_OBJECT(button), "menu", (gpointer)menu);
1312    
1313        /* draw a popup menu */
1314        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1315                       event->button, event->time);
1316        return TRUE;
1317      }
1318      return FALSE;
1319    }
1320    
1321    static void on_popup_destroy(GtkWidget *widget, gpointer user_data ) {
1322      GtkWidget *menu = g_object_get_data(G_OBJECT(widget), "menu");
1323      if(menu) gtk_widget_destroy(menu);
1324    }
1325    #endif
1326    
1327    #ifdef PRESET_PICKER_DIALOG
1328    
1329    enum {
1330      PRESET_PICKER_COL_ICON = 0,
1331      PRESET_PICKER_COL_NAME,
1332      PRESET_PICKER_COL_ID,
1333      PRESET_PICKER_COL_CB,
1334      PRESET_PICKER_NUM_COLS
1335    };
1336    
1337    static void preset_picker_add(GtkListStore *store,  appdata_t *appdata,
1338                           GdkPixbuf *icon, char *menu_str,
1339                           void(*func)(GtkWidget*, gpointer), gint id) {
1340      GtkTreeIter     iter;
1341    
1342      /* Append a row and fill in some data */
1343      gtk_list_store_append (store, &iter);
1344    
1345      gtk_list_store_set(store, &iter,
1346                         PRESET_PICKER_COL_ICON, icon,
1347                         PRESET_PICKER_COL_NAME, menu_str,
1348                         PRESET_PICKER_COL_ID, id,
1349                         PRESET_PICKER_COL_CB, func,
1350                         -1);
1351    }
1352    
1353    static void on_preset_picker_activated(GtkTreeView        *treeview,
1354                                    GtkTreePath        *path,
1355                                    GtkTreeViewColumn  *col,
1356                                    gpointer            userdata) {
1357      GtkTreeIter   iter;
1358      GtkTreeModel *model = gtk_tree_view_get_model(treeview);
1359    
1360      if(gtk_tree_model_get_iter(model, &iter, path)) {
1361        gint id;
1362        void(*func)(GtkWidget*, gpointer);
1363        gtk_tree_model_get(model, &iter,
1364                           PRESET_PICKER_COL_ID, &id,
1365                           PRESET_PICKER_COL_CB, &func,
1366                           -1);
1367    
1368        /* set id on widget as callbacks expect it this way */
1369        g_object_set_data(G_OBJECT(treeview), "id", (gpointer)id);
1370        func(GTK_WIDGET(treeview), userdata);
1371    
1372        gtk_dialog_response(GTK_DIALOG(gtk_widget_get_toplevel(
1373                        GTK_WIDGET(treeview))), GTK_RESPONSE_ACCEPT);
1374    
1375      }
1376    }
1377    
1378    static GtkWidget *preset_picker_create(appdata_t *appdata,
1379                                    GtkWidget *lat_entry, GtkWidget *lon_entry) {
1380      GtkCellRenderer *renderer;
1381      GtkListStore    *store;
1382    
1383      GtkWidget *view = gtk_tree_view_new();
1384    
1385      g_object_set_data(G_OBJECT(view), "lat_entry", (gpointer)lat_entry);
1386      g_object_set_data(G_OBJECT(view), "lon_entry", (gpointer)lon_entry);
1387    
1388      /* --- "Icon" column --- */
1389      renderer = gtk_cell_renderer_pixbuf_new();
1390      gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
1391          -1, "Icon", renderer, "pixbuf", PRESET_PICKER_COL_ICON, NULL);
1392    
1393      /* --- "Name" column --- */
1394      renderer = gtk_cell_renderer_text_new();
1395      g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
1396      GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
1397                     "Name", renderer, "text", PRESET_PICKER_COL_NAME, NULL);
1398      gtk_tree_view_column_set_expand(column, TRUE);
1399      gtk_tree_view_insert_column(GTK_TREE_VIEW(view), column, -1);
1400    
1401      store = gtk_list_store_new(PRESET_PICKER_NUM_COLS,
1402                                 GDK_TYPE_PIXBUF,
1403                                 G_TYPE_STRING,
1404                                 G_TYPE_INT,
1405                                 G_TYPE_POINTER);
1406    
1407      if(pos_valid(gps_get_pos(appdata)))
1408        preset_picker_add(store, appdata, icon_get(ICON_POS, 18),
1409                          _("GPS position"), cb_gps, 0);
1410    
1411      if(pos_valid(&appdata->home))
1412        preset_picker_add(store, appdata, icon_get(ICON_POS, 21),
1413                          _("Home"), cb_gps, 1);
1414    
1415      location_t *location = appdata->location;
1416      gint id = 2;
1417      while(location) {
1418        if(pos_valid(&location->pos))
1419          preset_picker_add(store, appdata, icon_get(ICON_POS, 21),
1420                            location->name, cb_gps, id);
1421    
1422        id++;
1423        location = location->next;
1424      }
1425    
1426      if(pos_valid(&appdata->geomath))
1427        preset_picker_add(store, appdata, icon_get(ICON_POS, 19),
1428                          _("Geomath projection"), cb_geomath, 0);
1429    #ifdef ENABLE_OSM_GPS_MAP
1430      if(pos_valid(&appdata->map.pos))
1431        preset_picker_add(store, appdata, icon_get(ICON_POS, 20),
1432                          _("Map position"), cb_map, 0);
1433    #endif
1434    
1435      if(appdata->cur_cache) {
1436        cache_t *cache = appdata->cur_cache;
1437    
1438        char *name = cache->name;
1439        if(!name) name = cache->id;
1440    
1441        /* original cache position */
1442        if(pos_valid(&cache->pos))
1443          preset_picker_add(store, appdata, icon_get(ICON_POS, cache->type + 6),
1444                            name, cb_cache, 0);
1445    
1446        /* overwritten cache position */
1447        if(cache->notes && pos_valid(&cache->notes->pos))
1448          preset_picker_add(store, appdata, icon_get(ICON_POS, cache->type + 6),
1449                            _("Modified coordinate"), cb_cache, 1);
1450    
1451        wpt_t *wpt = cache->wpt;
1452        gint id = 2;
1453        while(wpt) {
1454          if(pos_valid(&wpt->pos)) {
1455            GdkPixbuf *icon = NULL;
1456            if(wpt->sym != WPT_SYM_UNKNOWN)
1457              icon = icon_get(ICON_POS, wpt->sym);
1458    
1459            char *name = wpt->desc;
1460            if(!name) name = wpt->cmt;
1461            if(!name) name = wpt->id;
1462    
1463            preset_picker_add(store, appdata, icon, name, cb_cache, id);
1464          }
1465          id++;
1466          wpt = wpt->next;
1467        }
1468      }
1469    
1470      gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
1471      g_object_unref(store);
1472    
1473      /* make list react on clicks */
1474      g_signal_connect(view, "row-activated",
1475                       (GCallback)on_preset_picker_activated, appdata);
1476    
1477      /* put this inside a scrolled view */
1478    #ifndef USE_PANNABLE_AREA
1479      GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1480      gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1481                                     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1482      gtk_container_add(GTK_CONTAINER(scrolled_window), view);
1483      return scrolled_window;
1484    #else
1485      GtkWidget *pannable_area = hildon_pannable_area_new();
1486      gtk_container_add(GTK_CONTAINER(pannable_area), view);
1487      return pannable_area;
1488    #endif
1489    }
1490    
1491    static gint on_preset_picker_button_press(GtkWidget *button,
1492                       GdkEventButton *event, gpointer data) {
1493      appdata_t *appdata = (appdata_t*)data;
1494    
1495      gpointer lat_entry = g_object_get_data(G_OBJECT(button), "lat_entry");
1496      gpointer lon_entry = g_object_get_data(G_OBJECT(button), "lon_entry");
1497    
1498      if(event->type == GDK_BUTTON_PRESS) {
1499        GtkWidget *dialog =
1500          gtk_dialog_new_with_buttons(_("Preset coordinates"),
1501              GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
1502                                      GTK_DIALOG_MODAL,
1503              GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1504              NULL);
1505    
1506        gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 210);
1507    
1508        gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1509                            preset_picker_create(appdata, lat_entry, lon_entry));
1510    
1511        gtk_widget_show_all(dialog);
1512        gtk_dialog_run(GTK_DIALOG(dialog));
1513        gtk_widget_destroy(dialog);
1514    
1515        return TRUE;
1516      }
1517      return FALSE;
1518    }
1519    #endif
1520    
1521    
1522    GtkWidget *preset_coordinate_picker(appdata_t *appdata,
1523                         GtkWidget *lat_entry, GtkWidget *lon_entry) {
1524    
1525      GtkWidget *button = gtk_button_new();
1526    
1527      gtk_button_set_image(GTK_BUTTON(button), icon_get_widget(ICON_POS, 17));
1528    
1529      gtk_widget_set_tooltip_text(button, _("Preset coordinates"));
1530    
1531      g_object_set_data(G_OBJECT(button), "lat_entry", (gpointer)lat_entry);
1532      g_object_set_data(G_OBJECT(button), "lon_entry", (gpointer)lon_entry);
1533    
1534    #ifndef PRESET_PICKER_DIALOG
1535      gtk_signal_connect(GTK_OBJECT(button), "button-press-event",
1536                         (GtkSignalFunc)on_popup_button_press, appdata);
1537    
1538      gtk_signal_connect(GTK_OBJECT(button), "destroy",
1539                         (GtkSignalFunc)on_popup_destroy, appdata);
1540    
1541      g_object_set_data(G_OBJECT(button), "menu",
1542                        popup_menu_create(appdata, lat_entry, lon_entry));
1543    #else
1544    #ifdef FREMANTLE
1545      hildon_gtk_widget_set_theme_size(button,
1546            (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1547    #endif
1548    
1549      gtk_signal_connect(GTK_OBJECT(button), "button-press-event",
1550                         (GtkSignalFunc)on_preset_picker_button_press, appdata);
1551    #endif
1552    
1553      return button;
1554    }
1555    
1556    GtkWidget *entry_new(void) {
1557    #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
1558      return gtk_entry_new();
1559    #else
1560      return hildon_entry_new(HILDON_SIZE_AUTO);
1561    #endif
1562    }
1563    
1564    gboolean pos_differ(pos_t *pos1, pos_t *pos2) {
1565      int lat1 = (60000 * pos1->lat)+0.5, lon1 = (60000 * pos1->lon)+0.5;
1566      int lat2 = (60000 * pos2->lat)+0.5, lon2 = (60000 * pos2->lon)+0.5;
1567    
1568      return((lat1 != lat2) || (lon1 != lon2));
1569    }
1570    
1571    gboolean pos_valid(pos_t *pos) {
1572      if(!pos) return FALSE;
1573      return(!isnan(pos->lat) && !isnan(pos->lon));
1574    }
1575    
1576    void misc_init(void) {
1577      g_signal_new ("changed", GTK_TYPE_BUTTON,
1578                    G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
1579                    g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1580    }
1581    
1582    void angle_str(char *str, int len, float angle) {
1583      snprintf(str, len, _("%.1f°"), angle);
1584    }
1585    
1586    float angle_parse(char *str) {
1587      float val;
1588    
1589      if(sscanf(str, _("%f°"), &val) != 1)
1590        val = NAN;
1591    
1592      return val;
1593    }
1594    
1595    #ifndef COORDINATE_PICKER
1596    static void callback_modified_angle(GtkWidget *widget, gpointer data ) {
1597      float i = angle_parse((char*)gtk_entry_get_text(GTK_ENTRY(widget)));
1598      mark(widget, !isnan(i));
1599    }
1600    #else
1601    static gint on_angle_picker_button_press(GtkWidget *button,
1602                       GdkEventButton *event, gpointer data) {
1603    
1604      if(event->type == GDK_BUTTON_PRESS) {
1605        GtkWidget *dialog =
1606          gtk_dialog_new_with_buttons(_("Direction"),
1607              GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
1608                                      GTK_DIALOG_MODAL,
1609              GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1610              _("Done"),        GTK_RESPONSE_ACCEPT,
1611              NULL);
1612    
1613        gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 210);
1614    
1615        int i, angle = (int)g_object_get_data(G_OBJECT(button), "angle");
1616    
1617        GtkWidget *anglew[3], *fracw;
1618    
1619        /* create xxx.x° */
1620        GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
1621    
1622        gtk_box_pack_start_defaults(GTK_BOX(hbox),
1623                    anglew[0] = digit_picker_create(0,3, (angle/1000)%10));
1624        gtk_box_pack_start_defaults(GTK_BOX(hbox),
1625                    anglew[1] = digit_picker_create(0,9, (angle/100)%10));
1626        gtk_box_pack_start_defaults(GTK_BOX(hbox),
1627                    anglew[2] = digit_picker_create(0,9, (angle/10)%10));
1628        gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" . "), FALSE, FALSE, 0);
1629        gtk_box_pack_start_defaults(GTK_BOX(hbox),
1630                    fracw = digit_picker_create(0,9, (angle/1)%10));
1631        gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("°"), FALSE, FALSE, 0);
1632    
1633        gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
1634    
1635        gtk_widget_show_all(dialog);
1636        if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1637    
1638          /* parse angle */
1639          for(angle=0,i=0;i<3;i++)
1640            angle = 10 * angle + picker_get(anglew[i]);
1641    
1642          angle = 10 * angle + picker_get(fracw);
1643    
1644          angle_entry_set(button, angle/10.0);
1645        }
1646    
1647        gtk_widget_destroy(dialog);
1648    
1649        return TRUE;
1650      }
1651      return FALSE;
1652    }
1653    #endif
1654    
1655    GtkWidget *angle_entry_new(float angle) {
1656      char str[32];
1657      angle_str(str, sizeof(str), angle);
1658    
1659    #ifndef COORDINATE_PICKER
1660      GtkWidget *widget = red_entry_new_with_text(str);
1661      g_signal_connect(G_OBJECT(widget), "changed",
1662                       G_CALLBACK(callback_modified_angle), NULL);
1663    #else
1664      GtkWidget *widget = gtk_button_new_with_label(str);
1665    
1666    #ifdef FREMANTLE
1667      hildon_gtk_widget_set_theme_size(widget,
1668            (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1669    #endif
1670      int angle_int = (int)roundf(angle*10.0);
1671      g_object_set_data(G_OBJECT(widget), "angle", (gpointer)angle_int);
1672      gtk_signal_connect(GTK_OBJECT(widget), "button-press-event",
1673                         (GtkSignalFunc)on_angle_picker_button_press, NULL);
1674    #endif
1675      return widget;
1676    }
1677    
1678    float angle_entry_get(GtkWidget *widget) {
1679    #ifndef COORDINATE_PICKER
1680      char *p = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
1681    #else
1682      char *p = (char*)gtk_button_get_label(GTK_BUTTON(widget));
1683    #endif
1684      return angle_parse(p);
1685    }
1686    
1687    void angle_entry_set(GtkWidget *widget, float angle) {
1688      char str[32];
1689      angle_str(str, sizeof(str)-1, angle);
1690    #ifndef COORDINATE_PICKER
1691      gtk_entry_set_text(GTK_ENTRY(widget), str);
1692    #else
1693      gtk_button_set_label(GTK_BUTTON(widget), str);
1694      int angle_int = (int)roundf(angle * 10.0);
1695      g_object_set_data(G_OBJECT(widget), "angle", (gpointer)angle_int);
1696      g_signal_emit_by_name(widget, "changed");
1697    #endif
1698    }
1699    

Legend:
Removed from v.34  
changed lines
  Added in v.226