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

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