/** * Navit, a modular navigation system. * Copyright (C) 2005-2008 Navit Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "debug.h" #include "destination.h" #include "navit.h" #include "item.h" #include "coord.h" #include "track.h" #include "country.h" #include "search.h" #include "projection.h" #include "navit_nls.h" #include "bookmarks.h" #define COL_COUNT 8 static struct search_param { struct navit *nav; struct mapset *ms; struct search_list *sl; struct attr attr; int partial; GtkWidget *entry_country, *entry_postal, *entry_city, *entry_district; GtkWidget *entry_street, *entry_number; GtkWidget *listbox; GtkWidget *treeview; GtkListStore *liststore; GtkTreeModel *liststore2; } search_param; static void button_map(GtkWidget *widget, struct search_param *search) { GtkTreePath *path; GtkTreeViewColumn *focus_column; struct pcoord *c=NULL; GtkTreeIter iter; gtk_tree_view_get_cursor(GTK_TREE_VIEW(search->treeview), &path, &focus_column); if(!path) return; if(!gtk_tree_model_get_iter(search->liststore2, &iter, path)) return; gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), &iter, COL_COUNT, &c, -1); if (c) { navit_set_center(search->nav, c, 1); } } static char *description(struct search_param *search, GtkTreeIter *iter) { char *desc,*car,*postal,*town,*street; char empty='\0'; gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), iter, 0, &car, -1); gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), iter, 1, &postal, -1); gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), iter, 2, &town, -1); gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), iter, 4, &street, -1); /* protect against nulls */ if (car==0) car=∅ if (postal==0) postal=∅ if (town==0) town=∅ if (street==0) street=∅ if (search->attr.type == attr_town_name) desc=g_strdup_printf("%s-%s %s", car, postal, town); else desc=g_strdup_printf("%s-%s %s, %s", car, postal, town, street); return desc; } static void button_destination(GtkWidget *widget, struct search_param *search) { struct pcoord *c=NULL; GtkTreeIter iter; char *desc; if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (search->liststore2), &iter)) return; gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), &iter, COL_COUNT, &c, -1); if (c) { desc=description(search, &iter); navit_set_destination(search->nav, c, desc, 1); g_free(desc); } } static void button_bookmark(GtkWidget *widget, struct search_param *search) { struct pcoord *c=NULL; GtkTreeIter iter; char *desc; struct attr attr; if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (search->liststore2), &iter)) return; gtk_tree_model_get (GTK_TREE_MODEL (search->liststore2), &iter, COL_COUNT, &c, -1); if (c) { navit_get_attr(search->nav, attr_bookmarks, &attr, NULL); desc=description(search, &iter); bookmarks_add_bookmark(attr.u.bookmarks, c, desc); g_free(desc); } } char **columns_text[] = { (char *[]){_n("Car"),_n("Iso2"),_n("Iso3"),_n("Country"),NULL}, (char *[]){_n("Car"),_n("Postal"),_n("Town"),_n("District"),NULL}, (char *[]){_n("Car"),_n("Postal"),_n("Town"),_n("District"),_n("Street"),NULL}, (char *[]){_n("Car"),_n("Postal"),_n("Town"),_n("District"),_n("Street"),_n("Number"),NULL}, }; static void set_columns(struct search_param *param, int mode) { GList *columns_list,*columns; char **column_text=columns_text[mode]; int i=0; columns_list=gtk_tree_view_get_columns(GTK_TREE_VIEW(param->treeview)); columns=columns_list; while (columns) { gtk_tree_view_remove_column(GTK_TREE_VIEW(param->treeview), columns->data); columns=g_list_next(columns); } g_list_free(columns_list); while (*column_text) { GtkCellRenderer *cell=gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (param->treeview),-1, navit_nls_gettext(*column_text), cell, "text", i, NULL); i++; column_text++; } } static void row_activated(GtkWidget *widget, GtkTreePath *p1, GtkTreeViewColumn *c, struct search_param *search) { GtkTreePath *path; GtkTreeViewColumn *focus_column; GtkTreeIter iter; GtkWidget *entry_widget; char *str; int column; dbg(lvl_debug,"enter"); gtk_tree_view_get_cursor(GTK_TREE_VIEW(search->treeview), &path, &focus_column); if(!path) return; if(!gtk_tree_model_get_iter(search->liststore2, &iter, path)) return; switch(search->attr.type) { case attr_country_all: entry_widget=search->entry_country; column=3; break; case attr_town_name: entry_widget=search->entry_city; column=2; break; case attr_street_name: entry_widget=search->entry_street; column=4; break; default: dbg(lvl_debug,"Unknown mode"); return; } gtk_tree_model_get(search->liststore2, &iter, column, &str, -1); dbg(lvl_debug,"str=%s", str); search->partial=0; gtk_entry_set_text(GTK_ENTRY(entry_widget), str); } static void tree_view_button_release(GtkWidget *widget, GdkEventButton *event, struct search_param *search) { GtkTreePath *path; GtkTreeViewColumn *column; gtk_tree_view_get_cursor(GTK_TREE_VIEW(search->treeview), &path, &column); gtk_tree_view_row_activated(GTK_TREE_VIEW(search->treeview), path, column); } static void next_focus(struct search_param *search, GtkWidget *widget) { if (widget == search->entry_country) gtk_widget_grab_focus(search->entry_city); if (widget == search->entry_city) gtk_widget_grab_focus(search->entry_street); if (widget == search->entry_street) gtk_widget_grab_focus(search->entry_number); } static void changed(GtkWidget *widget, struct search_param *search) { struct search_list_result *res; GtkTreeIter iter; search->attr.u.str=(char *)gtk_entry_get_text(GTK_ENTRY(widget)); printf("changed %s partial %d\n", search->attr.u.str, search->partial); if (widget == search->entry_country) { gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (search->liststore2), 3, GTK_SORT_ASCENDING); dbg(lvl_debug,"country"); search->attr.type=attr_country_all; set_columns(search, 0); } if (widget == search->entry_postal) { gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (search->liststore2), 1, GTK_SORT_ASCENDING); dbg(lvl_debug,"postal"); search->attr.type=attr_town_postal; if (strlen(search->attr.u.str) < 2) return; set_columns(search, 1); } if (widget == search->entry_city) { gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (search->liststore2), 2, GTK_SORT_ASCENDING); dbg(lvl_debug,"town"); search->attr.type=attr_town_name; if (strlen(search->attr.u.str) < 3) return; set_columns(search, 1); } if (widget == search->entry_street) { dbg(lvl_debug,"street"); search->attr.type=attr_street_name; // Searching for a street by just its first letter generates too many hits to be useful, // plus it causes the GUI to become unresponsive because the search is single-threaded. // So we only start searching once we have two letters. if (strlen(search->attr.u.str) < 2) return; set_columns(search, 2); } search_list_search(search->sl, &search->attr, search->partial); gtk_list_store_clear(search->liststore); while((res=search_list_get_result(search->sl))) { gtk_list_store_append(search->liststore,&iter); gtk_list_store_set(search->liststore,&iter,COL_COUNT,res->c,-1); if (widget == search->entry_country) { if (res->country) { gtk_list_store_set(search->liststore,&iter,0,res->country->car,-1); gtk_list_store_set(search->liststore,&iter,1,res->country->iso3,-1); gtk_list_store_set(search->liststore,&iter,2,res->country->iso2,-1); gtk_list_store_set(search->liststore,&iter,3,res->country->name,-1); } } else { if (res->country) gtk_list_store_set(search->liststore,&iter,0,res->country->car,-1); else gtk_list_store_set(search->liststore,&iter,0,"",-1); if (res->town) { gtk_list_store_set(search->liststore,&iter,1,res->town->common.postal,-1); gtk_list_store_set(search->liststore,&iter,2,res->town->common.town_name,-1); gtk_list_store_set(search->liststore,&iter,3,res->town->common.district_name,-1); } else { gtk_list_store_set(search->liststore,&iter,1,"",-1); gtk_list_store_set(search->liststore,&iter,2,"",-1); gtk_list_store_set(search->liststore,&iter,3,"",-1); } if (res->street) gtk_list_store_set(search->liststore,&iter,4,res->street->name,-1); else gtk_list_store_set(search->liststore,&iter,4,"",-1); } } if(! search->partial) { if( widget == search->entry_country ) { gtk_widget_set_sensitive(GTK_WIDGET(search->entry_city), TRUE); } if( widget == search->entry_city ) { gtk_widget_set_sensitive(GTK_WIDGET(search->entry_city), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_street), TRUE); } if( widget == search->entry_street ) { gtk_widget_set_sensitive(GTK_WIDGET(search->entry_city), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_street), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_number), TRUE); } } else { if( widget == search->entry_country ) { gtk_widget_set_sensitive(GTK_WIDGET(search->entry_city), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_street), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_number), FALSE); } if( widget == search->entry_city ) { gtk_widget_set_sensitive(GTK_WIDGET(search->entry_street), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_number), FALSE); } if( widget == search->entry_street ) { gtk_widget_set_sensitive(GTK_WIDGET(search->entry_number), FALSE); } } if (! search->partial) next_focus(search, widget); search->partial=1; } /* borrowed from gpe-login */ #define MAX_ARGS 8 static void parse_xkbd_args (const char *cmd, char **argv) { const char *p = cmd; char buf[strlen (cmd) + 1], *bufp = buf; int nargs = 0; int escape = 0, squote = 0, dquote = 0; while (*p) { if (escape) { *bufp++ = *p; escape = 0; } else { switch (*p) { case '\\': escape = 1; break; case '"': if (squote) *bufp++ = *p; else dquote = !dquote; break; case '\'': if (dquote) *bufp++ = *p; else squote = !squote; break; case ' ': if (!squote && !dquote) { *bufp = 0; if (nargs < MAX_ARGS) argv[nargs++] = strdup (buf); bufp = buf; break; } default: *bufp++ = *p; break; } } p++; } if (bufp != buf) { *bufp = 0; if (nargs < MAX_ARGS) argv[nargs++] = strdup (buf); } argv[nargs] = NULL; } int kbd_pid; static int spawn_xkbd (char *xkbd_path, char *xkbd_str) { #ifdef _WIN32 // AF FIXME for WIN32 #ifndef F_SETFD #define F_SETFD 2 #endif #else char *xkbd_args[MAX_ARGS + 1]; int fd[2]; char buf[256]; char c; int a = 0; size_t n; pipe (fd); kbd_pid = fork (); if (kbd_pid == 0) { close (fd[0]); if (dup2 (fd[1], 1) < 0) perror ("dup2"); close (fd[1]); if (fcntl (1, F_SETFD, 0)) perror ("fcntl"); xkbd_args[0] = (char *)xkbd_path; xkbd_args[1] = "-xid"; if (xkbd_str) parse_xkbd_args (xkbd_str, xkbd_args + 2); else xkbd_args[2] = NULL; execvp (xkbd_path, xkbd_args); perror (xkbd_path); _exit (1); } close (fd[1]); do { n = read (fd[0], &c, 1); if (n) { buf[a++] = c; } } while (n && (c != 10) && (a < (sizeof (buf) - 1))); if (a) { buf[a] = 0; return atoi (buf); } #endif return 0; } int destination_address(struct navit *nav) { GtkWidget *window2, *keyboard, *vbox, *table; GtkWidget *label_country; GtkWidget *label_postal, *label_city, *label_district; GtkWidget *label_street, *label_number; GtkWidget *button1,*button2,*button3; int i; struct search_param *search=&search_param; struct attr search_attr, country_name, *country_attr; struct tracking *tracking; struct country_search *cs; struct item *item; search->nav=nav; search->ms=navit_get_mapset(nav); search->sl=search_list_new(search->ms); window2 = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window2),_("Enter Destination")); gtk_window_set_wmclass (GTK_WINDOW (window2), "navit", "Navit"); gtk_window_set_default_size (GTK_WINDOW (window2),0,300); vbox = gtk_vbox_new(FALSE, 0); table = gtk_table_new(3, 8, FALSE); search->entry_country = gtk_entry_new(); label_country = gtk_label_new(_("Country")); search->entry_postal = gtk_entry_new(); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_postal), FALSE); label_postal = gtk_label_new(_("Zip Code")); search->entry_city = gtk_entry_new(); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_city), FALSE); label_city = gtk_label_new(_("City")); search->entry_district = gtk_entry_new(); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_district), FALSE); label_district = gtk_label_new(_("District/Township")); search->entry_street = gtk_entry_new(); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_street), FALSE); label_street = gtk_label_new(_("Street")); search->entry_number = gtk_entry_new(); gtk_widget_set_sensitive(GTK_WIDGET(search->entry_number), FALSE); label_number = gtk_label_new(_("Number")); search->treeview=gtk_tree_view_new(); search->listbox = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (search->listbox), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_tree_view_set_model (GTK_TREE_VIEW (search->treeview), NULL); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(search->listbox),search->treeview); { GType types[COL_COUNT+1]; for(i=0;iliststore=gtk_list_store_newv(COL_COUNT+1,types); search->liststore2=gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(search->liststore)); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (search->liststore2), 3, GTK_SORT_ASCENDING); gtk_tree_view_set_model (GTK_TREE_VIEW (search->treeview), GTK_TREE_MODEL(search->liststore2)); } button1 = gtk_button_new_with_label(_("Map")); button2 = gtk_button_new_with_label(_("Bookmark")); button3 = gtk_button_new_with_label(_("Destination")); gtk_table_attach(GTK_TABLE(table), label_country, 0, 1, 0, 1, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), label_postal, 1, 2, 0, 1, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), label_city, 2, 3, 0, 1, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), search->entry_country, 0, 1, 1, 2, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), search->entry_postal, 1, 2, 1, 2, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), search->entry_city, 2, 3, 1, 2, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), label_district, 0, 1, 2, 3, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), label_street, 1, 2, 2, 3, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), label_number, 2, 3, 2, 3, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), search->entry_district, 0, 1, 3, 4, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), search->entry_street, 1, 2, 3, 4, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), search->entry_number, 2, 3, 3, 4, 0, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), search->listbox, 0, 3, 4, 5, GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0); gtk_table_attach(GTK_TABLE(table), button1, 0, 1, 5, 6, GTK_FILL, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), button2, 1, 2, 5, 6, GTK_FILL, GTK_FILL, 0, 0); gtk_table_attach(GTK_TABLE(table), button3, 2, 3, 5, 6, GTK_FILL, GTK_FILL, 0, 0); g_signal_connect(G_OBJECT(search->entry_country), "changed", G_CALLBACK(changed), search); g_signal_connect(G_OBJECT(search->entry_postal), "changed", G_CALLBACK(changed), search); g_signal_connect(G_OBJECT(search->entry_city), "changed", G_CALLBACK(changed), search); g_signal_connect(G_OBJECT(search->entry_district), "changed", G_CALLBACK(changed), search); g_signal_connect(G_OBJECT(search->entry_street), "changed", G_CALLBACK(changed), search); g_signal_connect(G_OBJECT(search->entry_number), "changed", G_CALLBACK(changed), search); g_signal_connect(G_OBJECT(button1), "clicked", G_CALLBACK(button_map), search); g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button_bookmark), search); g_signal_connect(G_OBJECT(button3), "clicked", G_CALLBACK(button_destination), search); g_signal_connect(G_OBJECT(search->treeview), "button-release-event", G_CALLBACK(tree_view_button_release), search); g_signal_connect(G_OBJECT(search->treeview), "row_activated", G_CALLBACK(row_activated), search); gtk_widget_grab_focus(search->entry_city); gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0); keyboard=gtk_socket_new(); gtk_box_pack_end(GTK_BOX(vbox), keyboard, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(window2), vbox); #if 0 g_signal_connect(G_OBJECT(listbox), "select-row", G_CALLBACK(select_row), NULL); #endif gtk_widget_show_all(window2); #ifndef _WIN32 gtk_socket_steal(GTK_SOCKET(keyboard), spawn_xkbd("xkbd","-geometry 200x100")); #endif country_attr=country_default(); tracking=navit_get_tracking(nav); if (tracking && tracking_get_attr(tracking, attr_country_id, &search_attr, NULL)) country_attr=&search_attr; if (country_attr) { cs=country_search_new(country_attr, 0); item=country_search_get_item(cs); if (item && item_attr_get(item, attr_country_name, &country_name)) gtk_entry_set_text(GTK_ENTRY(search->entry_country), country_name.u.str); country_search_destroy(cs); } else { dbg(lvl_error,"warning: no default country found"); } search->partial=1; return 0; }