Add:Core:Improved searching
[navit-package] / navit / search.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 Navit Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19
20 #include <glib.h>
21 #include <string.h>
22 #include "debug.h"
23 #include "projection.h"
24 #include "map.h"
25 #include "mapset.h"
26 #include "coord.h"
27 #include "item.h"
28 #include "search.h"
29
30 struct search_list_level {
31         struct mapset *ms;
32         struct item *parent;
33         struct attr attr;
34         int partial;
35         int selected;
36         struct mapset_search *search;
37         GHashTable *hash;
38         GList *list,*curr,*last;
39 };
40
41 struct search_list {
42         struct mapset *ms;
43         int level;
44         struct search_list_level levels[4];
45         struct search_list_result result;
46 };
47
48 static guint
49 search_item_hash_hash(gconstpointer key)
50 {
51         const struct item *itm=key;
52         gconstpointer hashkey=(gconstpointer)(itm->id_hi^itm->id_lo);
53         return g_direct_hash(hashkey);
54 }
55
56 static gboolean
57 search_item_hash_equal(gconstpointer a, gconstpointer b)
58 {
59         const struct item *itm_a=a;
60         const struct item *itm_b=b;
61         if (item_is_equal_id(*itm_a, *itm_b))
62                 return TRUE;
63         return FALSE;
64 }
65
66 struct search_list *
67 search_list_new(struct mapset *ms)
68 {
69         struct search_list *ret;
70
71         ret=g_new0(struct search_list, 1);
72         ret->ms=ms;
73         
74         return ret;
75 }
76
77 static void search_list_search_free(struct search_list *sl, int level);
78
79 static int
80 search_list_level(enum attr_type attr_type)
81 {
82         switch(attr_type) {
83         case attr_country_all:
84         case attr_country_id:
85         case attr_country_iso2:
86         case attr_country_iso3:
87         case attr_country_car:
88         case attr_country_name:
89                 return 0;
90         case attr_town_postal:
91                 return 1;
92         case attr_town_name:
93                 return 1;
94         case attr_street_name:
95                 return 2;
96         default:
97                 return -1;
98         }
99 }
100
101 void
102 search_list_search(struct search_list *this_, struct attr *search_attr, int partial)
103 {
104         struct search_list_level *le;
105         int level=search_list_level(search_attr->type);
106         dbg(0,"level=%d\n", level);
107         if (level != -1) {
108                 this_->result.id=0;
109                 this_->level=level;
110                 le=&this_->levels[level];
111                 le->attr=*search_attr;
112                 if (search_attr->type != attr_country_id)
113                         le->attr.u.str=g_strdup(search_attr->u.str);
114                 search_list_search_free(this_, level);
115                 le->partial=partial;
116                 if (level > 0) {
117                         le=&this_->levels[level-1];
118                         le->curr=le->list;
119                 }
120                 dbg(1,"le=%p partial=%d\n", le, partial);
121         }
122 }
123
124 struct search_list_common *
125 search_list_select(struct search_list *this_, enum attr_type attr_type, int id, int mode)
126 {
127         int level=search_list_level(attr_type);
128         int num=0;
129         struct search_list_level *le;
130         struct search_list_common *slc;
131         GList *curr;
132         le=&this_->levels[level];
133         curr=le->list;
134         if (mode > 0 || !id)
135                 le->selected=mode;
136         dbg(1,"enter level=%d %d %d %p\n", level, id, mode, curr);
137         while (curr) {
138                 num++;
139                 if (! id || num == id) {
140                         slc=curr->data;
141                         slc->selected=mode;
142                         if (id) {
143                                 dbg(1,"found\n");
144                                 return slc;
145                         }
146                 }
147                 curr=g_list_next(curr);
148         }
149         dbg(1,"not found\n");
150         return NULL;
151 }
152
153 static struct search_list_country *
154 search_list_country_new(struct item *item)
155 {
156         struct search_list_country *ret=g_new0(struct search_list_country, 1);
157         struct attr attr;
158
159         ret->item=*item;
160         if (item_attr_get(item, attr_country_car, &attr))
161                 ret->car=g_strdup(attr.u.str);
162         if (item_attr_get(item, attr_country_iso2, &attr)) {
163                 ret->iso2=g_strdup(attr.u.str);
164                 ret->flag=g_strdup_printf("country_%s", ret->iso2);
165         }
166         if (item_attr_get(item, attr_country_iso3, &attr))
167                 ret->iso3=g_strdup(attr.u.str);
168         if (item_attr_get(item, attr_country_name, &attr))
169                 ret->name=g_strdup(attr.u.str);
170         return ret;
171 }
172
173 static void
174 search_list_country_destroy(struct search_list_country *this_)
175 {
176         g_free(this_->car);
177         g_free(this_->iso2);
178         g_free(this_->iso3);
179         g_free(this_->flag);
180         g_free(this_->name);
181         g_free(this_);
182 }
183
184 static struct search_list_town *
185 search_list_town_new(struct item *item)
186 {
187         struct search_list_town *ret=g_new0(struct search_list_town, 1);
188         struct attr attr;
189         struct coord c;
190         
191         ret->itemt=*item;
192         if (item_attr_get(item, attr_town_streets_item, &attr)) {
193                 dbg(1,"town_assoc 0x%x 0x%x\n", attr.u.item->id_hi, attr.u.item->id_lo);
194                 ret->item=*attr.u.item;
195         }
196         else
197                 ret->item=*item;
198         if (item_attr_get(item, attr_town_name, &attr))
199                 ret->name=map_convert_string(item->map,attr.u.str);
200         if (item_attr_get(item, attr_town_postal, &attr))
201                 ret->postal=map_convert_string(item->map,attr.u.str);
202         if (item_attr_get(item, attr_district_name, &attr))
203                 ret->district=map_convert_string(item->map,attr.u.str);
204         if (item_coord_get(item, &c, 1)) {
205                 ret->c=g_new(struct pcoord, 1);
206                 ret->c->x=c.x;
207                 ret->c->y=c.y;
208                 ret->c->pro = map_projection(item->map);
209         }
210         return ret;
211 }
212
213 static void
214 search_list_town_destroy(struct search_list_town *this_)
215 {
216         map_convert_free(this_->name);
217         map_convert_free(this_->postal);
218         if (this_->c)
219                 g_free(this_->c);
220         g_free(this_);
221 }
222
223 static struct search_list_street *
224 search_list_street_new(struct item *item)
225 {
226         struct search_list_street *ret=g_new0(struct search_list_street, 1);
227         struct attr attr;
228         struct coord c;
229         
230         ret->item=*item;
231         if (item_attr_get(item, attr_street_name, &attr))
232                 ret->name=map_convert_string(item->map, attr.u.str);
233         if (item_coord_get(item, &c, 1)) {
234                 ret->c=g_new(struct pcoord, 1);
235                 ret->c->x=c.x;
236                 ret->c->y=c.y;
237                 ret->c->pro = map_projection(item->map);
238         }
239         return ret;
240 }
241
242 static void
243 search_list_street_destroy(struct search_list_street *this_)
244 {
245         map_convert_free(this_->name);
246         if (this_->c)
247                 g_free(this_->c);
248         g_free(this_);
249 }
250
251
252 static void
253 search_list_result_destroy(int level, void *p)
254 {
255         switch (level) {
256         case 0:
257                 search_list_country_destroy(p);
258                 break;
259         case 1:
260                 search_list_town_destroy(p);
261                 break;
262         case 2:
263                 search_list_street_destroy(p);
264                 break;
265         }
266 }
267
268 static void
269 search_list_search_free(struct search_list *sl, int level)
270 {
271         struct search_list_level *le=&sl->levels[level];
272         GList *next,*curr;
273         if (le->search) {
274                 mapset_search_destroy(le->search);
275                 le->search=NULL;
276         }
277 #if 0 /* FIXME */
278         if (le->hash) {
279                 g_hash_table_destroy(le->hash);
280                 le->hash=NULL;
281         }
282 #endif
283         curr=le->list;
284         while (curr) {
285                 search_list_result_destroy(level, curr->data);
286                 next=g_list_next(curr);
287                 curr=next;
288         }
289         g_list_free(le->list);
290         le->list=NULL;
291         le->curr=NULL;
292         le->last=NULL;
293
294 }
295
296 static int
297 search_add_result(struct search_list_level *le, void *p)
298 {
299         if (! g_hash_table_lookup(le->hash, p)) {
300                 g_hash_table_insert(le->hash, p, (void *)1);    
301                 le->list=g_list_append(le->list, p);
302                 return 1;
303         }
304         return 0;
305 }
306
307 struct search_list_result *
308 search_list_get_result(struct search_list *this_)
309 {
310         struct search_list_level *le,*leu;
311         struct item *item;
312         int level=this_->level;
313
314         dbg(1,"enter\n");
315         le=&this_->levels[level];
316         dbg(1,"le=%p\n", le);
317         for (;;) {
318                 dbg(1,"le->search=%p\n", le->search);
319                 if (! le->search) {
320                         dbg(1,"partial=%d level=%d\n", le->partial, level);
321                         if (! level) 
322                                 le->parent=NULL;
323                         else {
324                                 leu=&this_->levels[level-1];
325                                 dbg(1,"leu->curr=%p\n", leu->curr);
326                                 for (;;) {
327                                         struct search_list_common *slc;
328                                         if (! leu->curr)
329                                                 return NULL;
330                                         le->parent=leu->curr->data;
331                                         leu->last=leu->curr;
332                                         leu->curr=g_list_next(leu->curr);
333                                         slc=(struct search_list_common *)(le->parent);
334                                         if (!slc)
335                                                 break;
336                                         if (slc->selected == leu->selected)
337                                                 break;
338                                 }
339                         }
340                         if (le->parent)
341                                 dbg(1,"mapset_search_new with item(%d,%d)\n", le->parent->id_hi, le->parent->id_lo);
342                         dbg(1,"attr=%s\n", attr_to_name(le->attr.type));        
343                         le->search=mapset_search_new(this_->ms, le->parent, &le->attr, le->partial);
344                         le->hash=g_hash_table_new(search_item_hash_hash, search_item_hash_equal);
345                 }
346                 dbg(1,"le->search=%p\n", le->search);
347                 item=mapset_search_get_item(le->search);
348                 dbg(1,"item=%p\n", item);
349                 if (item) {
350                         void *p=NULL;
351                         dbg(1,"id_hi=%d id_lo=%d\n", item->id_hi, item->id_lo);
352                         this_->result.country=NULL;
353                         this_->result.town=NULL;
354                         this_->result.street=NULL;
355                         this_->result.c=NULL;
356                         switch (level) {
357                         case 0:
358                                 p=search_list_country_new(item);
359                                 this_->result.country=p;
360                                 break;
361                         case 1:
362                                 p=search_list_town_new(item);
363                                 this_->result.country=this_->levels[0].last->data;
364                                 this_->result.town=p;
365                                 this_->result.c=this_->result.town->c;
366                                 break;
367                         case 2:
368                                 p=search_list_street_new(item);
369                                 this_->result.country=this_->levels[0].last->data;
370                                 this_->result.town=this_->levels[1].last->data;
371                                 this_->result.street=p;
372                                 this_->result.c=this_->result.street->c;
373                                 break;
374                         }
375                         if (p) {
376                                 if (search_add_result(le, p)) {
377                                         this_->result.id++;
378                                         return &this_->result;
379                                 } else 
380                                         search_list_result_destroy(level, p);
381                         }
382                 } else {
383                         mapset_search_destroy(le->search);
384                         le->search=NULL;
385                         g_hash_table_destroy(le->hash);
386                         if (! level)
387                                 break;
388                 }
389         }
390         return NULL;
391 }
392
393 void
394 search_list_destroy(struct search_list *this_)
395 {
396         g_free(this_);
397 }
398
399 void
400 search_init(void)
401 {
402 }