2 * Navit, a modular navigation system.
3 * Copyright (C) 2005-2010 Navit Team
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.
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.
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.
30 #include "projection.h"
32 #include "transform.h"
36 #include "bookmarks.h"
38 #include "navit_nls.h"
41 /* FIXME: Move this to support directory */
44 static int ftruncate(int fd, __int64 length)
46 HANDLE fh = (HANDLE)_get_osfhandle(fd);
47 if (!fh || _lseeki64(fd, length, SEEK_SET)) {
50 return SetEndOfFile(fh) ? 0 : -1;
58 GHashTable *bookmarks_hash;
59 GList *bookmarks_list;
62 struct bookmark_item_priv* clipboard;
63 struct bookmark_item_priv* root;
64 struct bookmark_item_priv* current;
66 //Refs to other objects
67 struct transformation *trans;
69 struct callback_list *attr_cbl;
73 struct bookmark_item_priv {
79 struct bookmark_item_priv *parent;
83 void bookmarks_move_root(struct bookmarks *this_) {
84 this_->current=this_->root;
85 this_->current->iter=g_list_first(this_->current->children);
86 dbg(2,"Root list have %u entries\n",g_list_length(this_->current->children));
89 void bookmarks_move_up(struct bookmarks *this_) {
90 if (this_->current->parent) {
91 this_->current=this_->current->parent;
92 this_->current->iter=this_->current->children;
96 int bookmarks_move_down(struct bookmarks *this_,const char* name) {
97 bookmarks_item_rewind(this_);
98 if (this_->current->children==NULL) {
101 while (this_->current->iter!=NULL) {
102 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
103 if (!strcmp(data->label,name)) {
104 this_->current=(struct bookmark_item_priv*)this_->current->iter->data;
105 this_->current->iter=g_list_first(this_->current->children);
106 dbg(2,"%s list have %u entries\n",this_->current->label,g_list_length(this_->current->children));
109 this_->current->iter=g_list_next(this_->current->iter);
114 void bookmarks_item_rewind(struct bookmarks* this_) {
115 this_->current->children=g_list_first(this_->current->children);
116 this_->current->iter=this_->current->children;
117 this_->current->iter=this_->current->children;
119 struct item* bookmarks_get_item(struct bookmarks* this_) {
120 struct item item,*ret;
121 if (this_->current->iter==NULL) {
125 item=((struct bookmark_item_priv*)this_->current->iter->data)->item;
126 this_->current->iter=g_list_next(this_->current->iter);
128 ret = map_rect_get_item_byid(this_->mr, item.id_hi, item.id_lo);
133 const char* bookmarks_item_cwd(struct bookmarks* this_) {
134 return this_->current->label;
137 static void bookmarks_clear_item(struct bookmark_item_priv *b_item) {
138 b_item->children=g_list_first(b_item->children);
139 while(b_item->children) {
140 bookmarks_clear_item((struct bookmark_item_priv*)b_item->children->data);
141 b_item->children=g_list_next(b_item->children);
143 g_free(b_item->label);
148 bookmarks_clear_hash(struct bookmarks *this_) {
150 map_rect_destroy(this_->mr);
152 bookmarks_clear_item(this_->root);
153 g_hash_table_destroy(this_->bookmarks_hash);
154 g_list_free(this_->bookmarks_list);
158 bookmarks_load_hash(struct bookmarks *this_) {
159 struct bookmark_item_priv *b_item;
167 map_rect_destroy(this_->mr);
169 this_->mr=map_rect_new(this_->bookmark, NULL);
171 this_->bookmarks_hash=g_hash_table_new(g_str_hash, g_str_equal);
172 this_->root=g_new0(struct bookmark_item_priv,1);
173 this_->root->type=type_none;
174 this_->root->parent=NULL;
175 this_->root->children=NULL;
176 bookmarks_move_root(this_);
178 while ((item=map_rect_get_item(this_->mr))) {
179 if (item->type != type_bookmark && item->type != type_bookmark_folder ) continue;
180 if (!item_attr_get(item, attr_path, &attr)) {
181 item_attr_get(item, attr_label, &attr);
183 item_coord_get(item, &c, 1);
185 b_item=g_new0(struct bookmark_item_priv,1);
188 b_item->label=g_strdup(attr.u.str);
189 b_item->type=item->type;
193 bookmarks_move_root(this_);
194 finder=b_item->label;
195 while ((pos=strchr(finder,'/'))) {
197 dbg(1,"Found path entry: %s\n",finder);
198 if (!bookmarks_move_down(this_,finder)) {
199 struct bookmark_item_priv *path_item=g_new0(struct bookmark_item_priv,1);
200 path_item->type=type_bookmark_folder;
201 path_item->parent=this_->current;
202 path_item->children=NULL;
203 path_item->label=g_strdup(finder);
205 this_->current->children=g_list_append(this_->current->children,path_item);
206 this_->current=path_item;
207 g_hash_table_insert(this_->bookmarks_hash,b_item->label,path_item);
208 this_->bookmarks_list=g_list_append(this_->bookmarks_list,path_item);
210 finder+=strlen(finder)+1;
212 copy_helper=g_strdup(finder);
214 b_item->label=copy_helper;
215 b_item->parent=this_->current;
217 g_hash_table_insert(this_->bookmarks_hash,b_item->label,b_item);
218 this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item);
219 this_->current->children=g_list_append(this_->current->children,b_item);
220 this_->current->children=g_list_first(this_->current->children);
221 dbg(1,"Added %s to %s and current list now %u long\n",b_item->label,this_->current->label,g_list_length(this_->current->children));
223 bookmarks_move_root(this_);
227 bookmarks_new(struct attr *parent, struct attr **attrs, struct transformation *trans) {
228 struct bookmarks *this_;
230 if (parent->type!=attr_navit) {
234 this_ = g_new0(struct bookmarks,1);
235 this_->attr_cbl=callback_list_new();
236 this_->parent=parent;
237 //this_->attrs=attr_list_dup(attrs);
240 this_->bookmark_file=g_strjoin(NULL, navit_get_user_data_directory(TRUE), "/bookmark.txt", NULL);
241 this_->working_file=g_strjoin(NULL, navit_get_user_data_directory(TRUE), "/bookmark.txt.tmp", NULL);
243 this_->clipboard=g_new0(struct bookmark_item_priv,1);
247 struct attr type={attr_type, {"textfile"}}, data={attr_data, {this_->bookmark_file}};
248 struct attr *attrs[]={&type, &data, NULL};
249 this_->bookmark=map_new(this_->parent, attrs);
250 if (!this_->bookmark)
252 bookmarks_load_hash(this_);
259 bookmarks_destroy(struct bookmarks *this_) {
261 bookmarks_clear_hash(this_);
263 map_destroy(this_->bookmark);
264 callback_list_destroy(this_->attr_cbl);
266 g_free(this_->bookmark_file);
267 g_free(this_->working_file);
269 g_free(this_->clipboard);
274 bookmarks_get_map(struct bookmarks *this_) {
275 return this_->bookmark;
278 enum projection bookmarks_get_projection(struct bookmarks *this_){
279 return map_projection(this_->bookmark);
282 bookmarks_add_callback(struct bookmarks *this_, struct callback *cb)
284 callback_list_add(this_->attr_cbl, cb);
288 bookmarks_store_bookmarks_to_file(struct bookmarks *this_, int limit,int replace) {
290 struct bookmark_item_priv *item,*parent_item;
294 GHashTable *dedup=g_hash_table_new_full(g_str_hash,g_str_equal,g_free,NULL);
296 f=fopen(this_->working_file, replace ? "w+" : "a+");
298 navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file"));
302 this_->bookmarks_list=g_list_first(this_->bookmarks_list);
303 while (this_->bookmarks_list) {
304 item=(struct bookmark_item_priv*)this_->bookmarks_list->data;
307 fullname=g_strdup(item->label);
308 while ((parent_item=parent_item->parent)) {
310 if (parent_item->label) {
311 pathHelper=g_strconcat(parent_item->label,"/",fullname,NULL);
313 fullname=g_strdup(pathHelper);
315 dbg(1,"full name: %s\n",fullname);
319 if (!g_hash_table_lookup(dedup,fullname)) {
320 g_hash_table_insert(dedup,fullname,fullname);
321 if (item->type == type_bookmark) {
322 prostr = projection_to_name(projection_mg);
323 if (fprintf(f,"%s%s%s0x%x %s0x%x type=%s label=\"%s\" path=\"%s\"\n",
324 prostr, *prostr ? ":" : "",
325 item->c.x >= 0 ? "":"-", item->c.x >= 0 ? item->c.x : -item->c.x,
326 item->c.y >= 0 ? "":"-", item->c.y >= 0 ? item->c.y : -item->c.y,
327 "bookmark", item->label,fullname)<1) {
332 if (item->type == type_bookmark_folder) {
333 prostr = projection_to_name(projection_mg);
334 if (fprintf(f,"%s%s%s0x%x %s0x%x type=%s label=\"%s\" path=\"%s\"\n",
335 prostr, *prostr ? ":" : "",
338 "bookmark_folder", item->label,fullname)<1) {
345 /* Limit could be zero, so we start decrementing it from zero and never reach 1
346 or it was bigger and we decreased it earlier. So when this counter becomes 1, we know
347 that we have enough entries in bookmarks file */
353 this_->bookmarks_list=g_list_next(this_->bookmarks_list);
358 g_hash_table_destroy(dedup);
361 map_rect_destroy(this_->mr);
365 unlink(this_->bookmark_file);
366 result=(rename(this_->working_file,this_->bookmark_file)==0);
369 navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file"));
375 * bookmarks_get_destination_file
377 * returns the name of the file used to store destinations with its
380 * arg: gboolean create: create the directory where the file is stored
381 * if it does not exist
384 bookmarks_get_destination_file(gboolean create)
386 return g_strjoin(NULL, navit_get_user_data_directory(create), "/destination.txt", NULL);
390 * bookmarks_get_center_file
392 * returns the name of the file used to store the center file with its
395 * arg: gboolean create: create the directory where the file is stored
396 * if it does not exist
399 bookmarks_get_center_file(gboolean create)
401 return g_strjoin(NULL, navit_get_user_data_directory(create), "/center.txt", NULL);
405 bookmarks_set_center_from_file(struct bookmarks *this_, char *file)
410 size_t line_size = 0;
412 struct coord *center;
414 f = fopen(file, "r");
417 getline(&line, &line_size, f);
420 center = transform_center(this_->trans);
421 pro = transform_get_projection(this_->trans);
422 coord_parse(g_strchomp(line), pro, center);
429 bookmarks_write_center_to_file(struct bookmarks *this_, char *file)
433 struct coord *center;
435 f = fopen(file, "w+");
437 center = transform_center(this_->trans);
438 pro = transform_get_projection(this_->trans);
439 coord_print(pro, center, f);
448 bookmarks_emit_dbus_signal(struct bookmarks *this_, struct pcoord *c, const char *description,int create)
450 struct attr attr1,attr2,attr3,attr4,cb,*attr_list[5];
452 attr1.type=attr_type;
453 attr1.u.str="bookmark";
454 attr2.type=attr_data;
455 attr2.u.str=create ? "create" : "delete";
456 attr3.type=attr_data;
457 attr3.u.str=(char *)description;
458 attr4.type=attr_coord;
465 if (navit_get_attr(this_->parent->u.navit, attr_callback_list, &cb, NULL))
466 callback_list_call_attr_4(cb.u.callback_list, attr_command, "dbus_send_signal", attr_list, NULL, &valid);
470 * Record the given set of coordinates as a bookmark
472 * @param navit The navit instance
473 * @param c The coordinate to store
474 * @param description A label which allows the user to later identify this bookmark
478 bookmarks_add_bookmark(struct bookmarks *this_, struct pcoord *pc, const char *description)
480 struct bookmark_item_priv *b_item=g_new0(struct bookmark_item_priv,1);
486 b_item->type=type_bookmark;
488 b_item->type=type_bookmark_folder;
490 b_item->label=g_strdup(description);
491 b_item->parent=this_->current;
492 b_item->children=NULL;
494 this_->current->children=g_list_first(this_->current->children);
495 this_->current->children=g_list_append(this_->current->children,b_item);
496 this_->bookmarks_list=g_list_first(this_->bookmarks_list);
497 this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item);
499 result=bookmarks_store_bookmarks_to_file(this_,0,0);
501 callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
502 bookmarks_clear_hash(this_);
503 bookmarks_load_hash(this_);
505 bookmarks_emit_dbus_signal(this_,&(b_item->c),description,TRUE);
511 bookmarks_cut_bookmark(struct bookmarks *this_, const char *label) {
512 if (bookmarks_copy_bookmark(this_,label)) {
513 return bookmarks_delete_bookmark(this_,label);
519 bookmarks_copy_bookmark(struct bookmarks *this_, const char *label) {
520 bookmarks_item_rewind(this_);
521 if (this_->current->children==NULL) {
524 while (this_->current->iter!=NULL) {
525 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
526 if (!strcmp(data->label,label)) {
527 this_->clipboard->c=data->c;
528 this_->clipboard->type=data->type;
529 this_->clipboard->item=data->item;
530 this_->clipboard->children=data->children;
531 if (!this_->clipboard->label) {
532 g_free(this_->clipboard->label);
534 this_->clipboard->label=g_strdup(data->label);
537 this_->current->iter=g_list_next(this_->current->iter);
542 bookmarks_paste_bookmark(struct bookmarks *this_) {
544 struct bookmark_item_priv* b_item;
546 if (!this_->clipboard->label) {
550 b_item=g_new0(struct bookmark_item_priv,1);
551 b_item->c.x=this_->clipboard->c.x;
552 b_item->c.y=this_->clipboard->c.y;
553 b_item->label=g_strdup(this_->clipboard->label);
554 b_item->type=this_->clipboard->type;
555 b_item->item=this_->clipboard->item;
556 b_item->parent=this_->current;
557 b_item->children=this_->clipboard->children;
559 g_hash_table_insert(this_->bookmarks_hash,b_item->label,b_item);
560 this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item);
561 this_->current->children=g_list_append(this_->current->children,b_item);
562 this_->current->children=g_list_first(this_->current->children);
564 result=bookmarks_store_bookmarks_to_file(this_,0,0);
566 callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
567 bookmarks_clear_hash(this_);
568 bookmarks_load_hash(this_);
575 bookmarks_delete_bookmark(struct bookmarks *this_, const char *label) {
578 bookmarks_item_rewind(this_);
579 if (this_->current->children==NULL) {
582 while (this_->current->iter!=NULL) {
583 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
584 if (!strcmp(data->label,label)) {
585 this_->bookmarks_list=g_list_first(this_->bookmarks_list);
586 this_->bookmarks_list=g_list_remove(this_->bookmarks_list,data);
588 result=bookmarks_store_bookmarks_to_file(this_,0,0);
590 callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
591 bookmarks_clear_hash(this_);
592 bookmarks_load_hash(this_);
594 bookmarks_emit_dbus_signal(this_,&(data->c),label,FALSE);
598 this_->current->iter=g_list_next(this_->current->iter);
605 bookmarks_rename_bookmark(struct bookmarks *this_, const char *oldName, const char* newName) {
608 bookmarks_item_rewind(this_);
609 if (this_->current->children==NULL) {
612 while (this_->current->iter!=NULL) {
613 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
614 if (!strcmp(data->label,oldName)) {
616 data->label=g_strdup(newName);
618 result=bookmarks_store_bookmarks_to_file(this_,0,0);
620 callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
621 bookmarks_clear_hash(this_);
622 bookmarks_load_hash(this_);
626 this_->current->iter=g_list_next(this_->current->iter);
632 struct former_destination{
638 static void free_former_destination(struct former_destination* former_destination){
639 g_free(former_destination->description);
640 g_free(former_destination);
644 static GList* read_former_destination_map_as_list(struct map *map){
648 struct former_destination *dest;
650 if (map && (mr=map_rect_new(map, NULL))) {
651 while ((item=map_rect_get_item(mr))) {
652 if (item->type != type_former_destination) continue;
653 dest = g_new(struct former_destination, 1);
654 dest->type=item->type;
655 item_attr_get(item, attr_label, &attr);
656 dest->description = g_strdup(attr.u.str);
657 item_coord_get(item, &(dest->c), 1);
658 list = g_list_prepend(list, dest);
660 map_rect_destroy(mr);
662 return g_list_reverse(list);
666 destination_equal(struct former_destination* dest1, struct former_destination* dest2)
668 if ((dest1->type == dest2->type) &&
669 (!strcmp(dest1->description, dest2->description)) &&
670 (coord_equal(&(dest1->c), &(dest2->c)))){
677 remove_destination_from_list(struct former_destination* dest_to_remove, GList* former_destinations)
679 GList* curr_el = former_destinations;
680 GList* prev_el = NULL;
681 struct former_destination* curr_dest;
684 curr_dest = curr_el->data;
685 if (destination_equal(dest_to_remove, curr_dest)){
686 free_former_destination(curr_dest);
687 curr_el = g_list_remove(curr_el, curr_dest);
690 curr_el = g_list_next(curr_el);
693 return g_list_first(prev_el);
697 write_former_destinations(GList* former_destinations, char *former_destination_file, enum projection proj)
700 GList* currdest = NULL;
701 struct former_destination *dest;
702 const char* prostr = projection_to_name(proj);
703 f=fopen(former_destination_file, "w");
705 for(currdest = former_destinations; currdest; currdest = g_list_next(currdest)){
706 dest = currdest->data;
707 if (dest->description)
708 fprintf(f,"type=%s label=\"%s\"\n", item_to_name(dest->type), dest->description);
710 fprintf(f,"type=%s\n", item_to_name(dest->type));
711 fprintf(f,"%s%s%s0x%x %s0x%x\n",
712 prostr, *prostr ? ":" : "",
713 dest->c.x >= 0 ? "":"-", dest->c.x >= 0 ? dest->c.x : -dest->c.x,
714 dest->c.y >= 0 ? "":"-", dest->c.y >= 0 ? dest->c.y : -dest->c.y);
719 dbg(0, "Error updating destinations file %s: %s\n", former_destination_file, strerror(errno));
724 * @param limit Limits the number of entries in the "backlog". Set to 0 for "infinite"
727 bookmarks_append_coord(struct map *former_destination_map, char *former_destination_file,
728 struct pcoord *c, enum item_type type, const char *description, int limit)
730 struct former_destination *new_dest;
731 GList* former_destinations = NULL;
732 GList* former_destinations_shortened = NULL;
733 int no_of_former_destinations;
735 former_destinations = read_former_destination_map_as_list(former_destination_map);
737 new_dest = g_new(struct former_destination, 1);
738 new_dest->type = type;
739 new_dest->description = g_strdup(description?description:_("Map Point"));
740 new_dest->c.x = c->x;
741 new_dest->c.y = c->y;
742 former_destinations = remove_destination_from_list(new_dest, former_destinations);
743 former_destinations = g_list_append(former_destinations, new_dest);
745 no_of_former_destinations = g_list_length(former_destinations);
746 if (limit > 0 && no_of_former_destinations > limit)
747 former_destinations_shortened = g_list_nth(former_destinations, no_of_former_destinations - limit);
749 former_destinations_shortened = former_destinations;
751 write_former_destinations(former_destinations_shortened, former_destination_file, map_projection(former_destination_map));
752 g_list_foreach(former_destinations, (GFunc) free_former_destination, NULL);
753 g_list_free(former_destinations);