]> err.no Git - mapper/blob - src/search.c
Move ProgressUpdateInfo struct
[mapper] / src / search.c
1 /*
2  * This file is part of mapper
3  *
4  * Copyright (C) 2007 Kaj-Michael Lang
5  * Copyright (C) 2006-2007 John Costigan.
6  *
7  * POI and GPS-Info code originally written by Cezary Jackiewicz.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  */
24
25 #include <config.h>
26
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <stddef.h>
32 #include <locale.h>
33 #include <math.h>
34 #include <errno.h>
35 #include <sys/wait.h>
36 #include <glib/gstdio.h>
37 #include <gtk/gtk.h>
38 #include <fcntl.h>
39 #include <libintl.h>
40 #include <locale.h>
41
42 #include "utils.h"
43 #include "poi.h"
44 #include "gps.h"
45 #include "gtkmap.h"
46 #include "mapper-types.h"
47 #include "ui-common.h"
48 #include "settings.h"
49 #include "poi-gui.h"
50 #include "osm.h"
51 #include "help.h"
52 #include "osm-db.h"
53 #include "search.h"
54 #include "dialogs.h"
55
56 typedef enum {
57         SBTN_INFO=1,
58         SBTN_GOTO=2,
59         SBTN_ROUTETO=3,
60         SBTN_EDIT=4,
61 } search_action_e;
62
63 typedef struct _search_dialog search_dialog;
64 struct _search_dialog {
65         GtkWidget *dialog;
66         GtkWidget *cmb_type;
67         GtkWidget *cmb_category;
68         GtkWidget *list;
69         GtkWidget *search_entry;
70         GtkWidget *label;
71         GtkListStore *store;
72         gdouble lat;
73         gdouble lon;
74         mapper_search_type stype;
75 };
76
77 static search_dialog sd;
78 static GSList *search_list=NULL;
79 static GtkListStore *search_model=NULL;
80
81 static gboolean
82 search_do_cb(GtkWidget *widget, gpointer data)
83 {
84 gchar *st;
85 guint cid;
86 GtkTreeIter iter;
87 guint slen;
88 search_dialog *s=(search_dialog *)data;
89 gboolean sres=FALSE;
90 slen=strlen(gtk_entry_get_text(GTK_ENTRY(s->search_entry)));
91
92 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(s->cmb_category), &iter)==TRUE)
93         gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(s->cmb_category)), &iter, 0, &cid, -1);
94 else
95         cid=-1;
96 if (s->stype==SEARCH_TYPE_POI && cid==-1 && slen<2) {
97 #ifdef WITH_HILDON
98         hildon_banner_show_information(s->dialog, NULL, _("Query string too short. Minimum 2 characters."));
99 #else
100         popup_error(s->dialog, _("Query string too short. Minimum 2 characters."));
101 #endif
102         return TRUE;
103 } else if (slen<2 && s->stype!=SEARCH_TYPE_POI) {
104 #ifdef WITH_HILDON
105         hildon_banner_show_information(s->dialog, NULL, _("Query string too short. Minimum 2 characters."));
106 #else
107         popup_error(s->dialog, _("Query string too short. Minimum 2 characters."));
108 #endif
109         return TRUE;
110 }
111
112 st=g_strdup(gtk_entry_get_text(GTK_ENTRY(s->search_entry)));
113
114 switch (s->stype) {
115         case SEARCH_TYPE_POI:
116                 MACRO_BANNER_SHOW_INFO(s->dialog, _("Searching POIs..."));
117                 sres=poi_search((cid==-1 && slen>0) ? POI_SEARCH_TEXT : (cid!=-1 && slen==0) ? POI_SEARCH_CAT : POI_SEARCH_TEXT_CAT, s->lat, s->lon, st, cid, &s->store);
118         break;
119         case SEARCH_TYPE_WAY:
120                 MACRO_BANNER_SHOW_INFO(s->dialog, _("Searching streets..."));
121                 sres=osm_way_search(s->lat, s->lon, st, &s->store);
122         break;
123         case SEARCH_TYPE_PLACE:
124                 sres=osm_place_search(s->lat, s->lon, st, &s->store);
125         break;
126 }
127
128 if (sres==TRUE) {
129         if (!g_slist_find_custom(search_list, st, (GCompareFunc)strcmp)) {
130                 search_list = g_slist_prepend(search_list, g_strdup(st));
131                 gtk_list_store_insert_with_values(search_model, &iter, INT_MAX, 0, st, -1);
132         }
133
134         gtk_tree_view_set_model(GTK_TREE_VIEW(s->list), GTK_TREE_MODEL(s->store));
135         g_object_unref(G_OBJECT(s->store));
136 } else {
137         s->store=NULL;
138         gtk_tree_view_set_model(GTK_TREE_VIEW(s->list), GTK_TREE_MODEL(s->store));
139 }
140
141 g_free(st);
142 return TRUE;
143 }
144
145 static gboolean
146 search_get_list_item_latlon(GtkWidget *list, gdouble *lat, gdouble *lon)
147 {
148 GtkTreeIter iter;
149 GtkTreeModel *model;
150
151 if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), NULL, &iter))
152         return FALSE;
153
154 model=gtk_tree_view_get_model(GTK_TREE_VIEW(list)); 
155 if (!model)
156         return FALSE;
157
158 gtk_tree_model_get(model, &iter, ITEM_LAT, lat, -1);
159 gtk_tree_model_get(model, &iter, ITEM_LON, lon, -1);
160 return TRUE;
161 }
162
163 static guint
164 search_get_list_item_id(GtkWidget *list)
165 {
166 guint id;
167 GtkTreeIter iter;
168 GtkTreeModel *model;
169
170 if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), NULL, &iter))
171         return 0;
172
173 model=gtk_tree_view_get_model(GTK_TREE_VIEW(list)); 
174 if (!model)
175         return 0;
176
177 gtk_tree_model_get(model, &iter, ITEM_ID, &id, -1);
178 return id;
179 }
180
181 static void
182 search_set_dialog_mode(search_dialog *s)
183 {
184 s->stype=gtk_combo_box_get_active(GTK_COMBO_BOX(s->cmb_type));
185 switch (s->stype) {
186         case SEARCH_TYPE_POI:
187                 gtk_widget_show(s->cmb_category);
188         break;
189         case SEARCH_TYPE_WAY:
190         case SEARCH_TYPE_PLACE:
191                 gtk_widget_hide(s->cmb_category);
192         break;
193         default:
194                 g_assert_not_reached();
195         break;
196 }
197 }
198
199 static gboolean
200 search_type_changed_cb(GtkWidget *widget, gpointer data)
201 {
202 search_dialog *s=(search_dialog *)data;
203 search_set_dialog_mode(s);
204 if (s->store)
205         gtk_list_store_clear(s->store);
206 s->store=NULL;
207 gtk_tree_view_set_model(GTK_TREE_VIEW(s->list), GTK_TREE_MODEL(s->store));
208 return TRUE;
209 }
210
211 static void
212 search_item_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
213 {
214 search_dialog *s=(search_dialog *)data;
215 gdouble lat, lon;
216 GtkTreeIter iter;
217 GtkTreeModel *model;
218
219 if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)), NULL, &iter))
220         return;
221
222 model=gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)); 
223 if (!model)
224         return;
225
226 gtk_tree_model_get(model, &iter, ITEM_LAT, &lat, -1);
227 gtk_tree_model_get(model, &iter, ITEM_LON, &lon, -1);
228
229 gtk_map_set_center_latlon(_map, lat, lon);
230 }
231
232 gboolean
233 mapper_search_dialog(mapper_search_type stype, gdouble lat, gdouble lon)
234 {
235 GtkWidget *sw, *hbox, *label, *btn_search;
236 GtkTreeViewColumn *column;
237 GtkCellRenderer *renderer;
238 GtkEntryCompletion *search_comp;
239 gint res;
240
241 sd.store=NULL;
242 sd.lat=lat;
243 sd.lon=lon;
244 sd.stype=stype;
245
246 sd.dialog = gtk_dialog_new_with_buttons(_("Search"),
247                         GTK_WINDOW(_window),
248                         GTK_DIALOG_MODAL,
249                         _("Info"), SBTN_INFO,
250                         _("Goto"), SBTN_GOTO,
251                         _("Route to"), SBTN_ROUTETO,
252                         _("Edit"), SBTN_EDIT,
253                         GTK_STOCK_CLOSE, GTK_RESPONSE_REJECT, 
254                         NULL);
255
256 if (search_model==NULL)
257         search_model=gtk_list_store_new(1, G_TYPE_STRING);
258
259 gtk_window_set_default_size(GTK_WINDOW(sd.dialog), 500, 300);
260
261 hbox = gtk_hbox_new(FALSE, 4),
262 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(sd.dialog)->vbox), hbox, FALSE, FALSE, 0);
263
264 label=gtk_label_new(_("Search:"));
265 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
266
267 sd.cmb_type=gtk_combo_box_new_text();
268 gtk_combo_box_append_text(GTK_COMBO_BOX(sd.cmb_type), "POI");
269 gtk_combo_box_append_text(GTK_COMBO_BOX(sd.cmb_type), "Street");
270 gtk_combo_box_append_text(GTK_COMBO_BOX(sd.cmb_type), "Place");
271 gtk_box_pack_start(GTK_BOX(hbox), sd.cmb_type, FALSE, FALSE, 0);
272
273 sd.cmb_category = category_combo_new();
274 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(sd.dialog)->vbox), sd.cmb_category, FALSE, FALSE, 0);
275 poi_category_combo_populate(sd.cmb_category, 0, TRUE);
276
277 sd.search_entry=gtk_entry_new();
278 search_comp=gtk_entry_completion_new();
279 gtk_entry_completion_set_model(search_comp, GTK_TREE_MODEL(search_model));
280 gtk_entry_completion_set_text_column(search_comp, 0);
281 gtk_entry_set_completion(GTK_ENTRY(sd.search_entry), search_comp);
282 gtk_box_pack_start(GTK_BOX(hbox), sd.search_entry, TRUE, TRUE, 0);
283
284 btn_search=gtk_button_new_with_label(_("Search"));
285 gtk_box_pack_start(GTK_BOX(hbox), btn_search, FALSE, FALSE, 0);
286
287 sw = gtk_scrolled_window_new(NULL, NULL);
288 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
289 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
290 #if defined(WITH_DEVICE_770) && defined(WITH_HILDON_NEW)
291 hildon_helper_set_thumb_scrollbar(sw, TRUE);
292 #endif
293 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(sd.dialog)->vbox), sw, TRUE, TRUE, 0);
294
295 sd.list=gtk_tree_view_new();
296 gtk_tree_selection_set_mode(gtk_tree_view_get_selection (GTK_TREE_VIEW(sd.list)), GTK_SELECTION_SINGLE);
297 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(sd.list), TRUE);
298 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(sd.list), TRUE);
299
300 renderer = gtk_cell_renderer_text_new();
301 column = gtk_tree_view_column_new_with_attributes(_("Location"), renderer, "text", ITEM_LATLON, NULL);
302 gtk_tree_view_append_column(GTK_TREE_VIEW(sd.list), column);
303
304 renderer = gtk_cell_renderer_text_new();
305 column = gtk_tree_view_column_new_with_attributes(_("Distance"), renderer, "text", ITEM_DIST, NULL);
306 gtk_tree_view_column_set_sort_column_id (column, ITEM_DIST);
307 gtk_tree_view_append_column(GTK_TREE_VIEW(sd.list), column);
308
309 renderer = gtk_cell_renderer_text_new();
310 column = gtk_tree_view_column_new_with_attributes(_("Label"), renderer, "text", ITEM_LABEL, NULL);
311 gtk_tree_view_column_set_sort_column_id (column, ITEM_LABEL);
312 gtk_tree_view_append_column(GTK_TREE_VIEW(sd.list), column);
313
314 renderer = gtk_cell_renderer_text_new();
315 column = gtk_tree_view_column_new_with_attributes(_("Category"), renderer, "text", ITEM_CATLAB, NULL);
316 gtk_tree_view_column_set_sort_column_id (column, ITEM_CATLAB);
317 gtk_tree_view_append_column(GTK_TREE_VIEW(sd.list), column);
318
319 gtk_tree_view_set_search_column (GTK_TREE_VIEW(sd.list), ITEM_LABEL);
320 gtk_container_add(GTK_CONTAINER(sw), sd.list);
321
322 sd.label=gtk_label_new("");
323 gtk_box_pack_start(GTK_BOX(hbox), sd.label, FALSE, FALSE, 0);
324
325 g_signal_connect(G_OBJECT(btn_search), "clicked", G_CALLBACK(search_do_cb), &sd);
326 g_signal_connect(G_OBJECT(sd.cmb_type), "changed", G_CALLBACK(search_type_changed_cb), &sd);
327 g_signal_connect(G_OBJECT(sd.list), "row-activated", G_CALLBACK(search_item_activated_cb), &sd);
328
329 gtk_widget_show_all(sd.dialog);
330 gtk_combo_box_set_active(GTK_COMBO_BOX(sd.cmb_type), sd.stype);
331 search_set_dialog_mode(&sd);
332
333 while ((res=gtk_dialog_run(GTK_DIALOG(sd.dialog)))!=GTK_RESPONSE_REJECT) {
334         gdouble lat, lon;
335
336         switch (res) {
337         case SBTN_INFO:
338                 switch (sd.stype) {
339                         case SEARCH_TYPE_POI:
340                                 poi_info_dialog(sd.dialog, search_get_list_item_id(sd.list));
341                         break;
342                         default:
343                                 popup_error(sd.dialog, _("No information available."));
344                         break;
345                 }
346         break;
347         case SBTN_GOTO:
348                 if (search_get_list_item_latlon(sd.list, &lat, &lon)==FALSE) {
349 #ifdef WITH_HILDON
350                         hildon_banner_show_information(sd.dialog, NULL, _("Select a location from the list."));
351 #else
352                         popup_error(sd.dialog, _("Select a location from the list."));
353 #endif
354                 } else {
355                         gtk_map_set_center_latlon(_map, lat, lon);
356                 }
357                 continue;
358         break;
359         case SBTN_ROUTETO:
360                 if (search_get_list_item_latlon(sd.list, &lat, &lon)==FALSE) {
361 #ifdef WITH_HILDON
362                         hildon_banner_show_information(sd.dialog, NULL, _("Select a location from the list."));
363 #else
364                         popup_error(sd.dialog, _("Select a location from the list."));
365 #endif
366                 } else {
367                         position_set(_dest, TRUE, lat, lon, 0);
368                 }
369                 continue;
370         break;
371         case SBTN_EDIT:
372                 switch (sd.stype) {
373                         case SEARCH_TYPE_POI: {
374                                 poi_info *p=NULL;
375                                 guint id;
376                 
377                                 id=search_get_list_item_id(sd.list);
378                                 if (id>0) {
379                                         p=poi_get_by_id(id);
380                                         if (p) {
381                                                 gtk_widget_destroy(sd.dialog);
382                                                 poi_edit_dialog(ACTION_EDIT_POI, p);
383                                                 return TRUE; /* XXX: for now, this is not nice.. */
384                                         } else {
385                                                 popup_error(sd.dialog, _("Failed to fetch POI for editing."));
386                                         }
387                                 }
388                         }
389                         break;
390                         default:
391                                 popup_error(sd.dialog, _("Only POIs can be modified."));
392                         break;
393                 }
394                 continue;
395         break;
396         default:
397                 break;
398         break;
399         }
400 }
401
402 gtk_widget_destroy(sd.dialog);
403 return TRUE;
404 }
405