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 int bookmarks_get_bookmark_count(struct bookmarks* this_) {
135 bookmarks_item_rewind(this_);
136 while (this_->current->iter!=NULL) {
137 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
138 if (data->type == type_bookmark) {
141 this_->current->iter=g_list_next(this_->current->iter);
146 const char* bookmarks_item_cwd(struct bookmarks* this_) {
147 return this_->current->label;
150 static void bookmarks_clear_item(struct bookmark_item_priv *b_item) {
151 b_item->children=g_list_first(b_item->children);
152 while(b_item->children) {
153 bookmarks_clear_item((struct bookmark_item_priv*)b_item->children->data);
154 b_item->children=g_list_next(b_item->children);
156 g_free(b_item->label);
161 bookmarks_clear_hash(struct bookmarks *this_) {
163 map_rect_destroy(this_->mr);
165 bookmarks_clear_item(this_->root);
166 g_hash_table_destroy(this_->bookmarks_hash);
167 g_list_free(this_->bookmarks_list);
171 bookmarks_load_hash(struct bookmarks *this_) {
172 struct bookmark_item_priv *b_item;
180 map_rect_destroy(this_->mr);
182 this_->mr=map_rect_new(this_->bookmark, NULL);
184 this_->bookmarks_hash=g_hash_table_new(g_str_hash, g_str_equal);
185 this_->root=g_new0(struct bookmark_item_priv,1);
186 this_->root->type=type_none;
187 this_->root->parent=NULL;
188 this_->root->children=NULL;
189 bookmarks_move_root(this_);
191 while ((item=map_rect_get_item(this_->mr))) {
192 if (item->type != type_bookmark && item->type != type_bookmark_folder ) continue;
193 if (!item_attr_get(item, attr_path, &attr)) {
194 item_attr_get(item, attr_label, &attr);
196 item_coord_get(item, &c, 1);
198 b_item=g_new0(struct bookmark_item_priv,1);
201 b_item->label=g_strdup(attr.u.str);
202 b_item->type=item->type;
206 bookmarks_move_root(this_);
207 finder=b_item->label;
208 while ((pos=strchr(finder,'/'))) {
210 dbg(1,"Found path entry: %s\n",finder);
211 if (!bookmarks_move_down(this_,finder)) {
212 struct bookmark_item_priv *path_item=g_new0(struct bookmark_item_priv,1);
213 path_item->type=type_bookmark_folder;
214 path_item->parent=this_->current;
215 path_item->children=NULL;
216 path_item->label=g_strdup(finder);
218 this_->current->children=g_list_append(this_->current->children,path_item);
219 this_->current=path_item;
220 g_hash_table_insert(this_->bookmarks_hash,b_item->label,path_item);
221 this_->bookmarks_list=g_list_append(this_->bookmarks_list,path_item);
223 finder+=strlen(finder)+1;
225 copy_helper=g_strdup(finder);
227 b_item->label=copy_helper;
228 b_item->parent=this_->current;
230 g_hash_table_insert(this_->bookmarks_hash,b_item->label,b_item);
231 this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item);
232 this_->current->children=g_list_append(this_->current->children,b_item);
233 this_->current->children=g_list_first(this_->current->children);
234 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));
236 bookmarks_move_root(this_);
240 bookmarks_new(struct attr *parent, struct attr **attrs, struct transformation *trans) {
241 struct bookmarks *this_;
243 if (parent->type!=attr_navit) {
247 this_ = g_new0(struct bookmarks,1);
248 this_->attr_cbl=callback_list_new();
249 this_->parent=parent;
250 //this_->attrs=attr_list_dup(attrs);
253 this_->bookmark_file=g_strjoin(NULL, navit_get_user_data_directory(TRUE), "/bookmark.txt", NULL);
254 this_->working_file=g_strjoin(NULL, navit_get_user_data_directory(TRUE), "/bookmark.txt.tmp", NULL);
256 this_->clipboard=g_new0(struct bookmark_item_priv,1);
260 struct attr type={attr_type, {"textfile"}}, data={attr_data, {this_->bookmark_file}};
261 struct attr *attrs[]={&type, &data, NULL};
262 this_->bookmark=map_new(this_->parent, attrs);
263 if (!this_->bookmark)
265 bookmarks_load_hash(this_);
272 bookmarks_destroy(struct bookmarks *this_) {
274 bookmarks_clear_hash(this_);
276 map_destroy(this_->bookmark);
277 callback_list_destroy(this_->attr_cbl);
279 g_free(this_->bookmark_file);
280 g_free(this_->working_file);
282 g_free(this_->clipboard);
287 bookmarks_get_map(struct bookmarks *this_) {
288 return this_->bookmark;
291 enum projection bookmarks_get_projection(struct bookmarks *this_){
292 return map_projection(this_->bookmark);
295 bookmarks_add_callback(struct bookmarks *this_, struct callback *cb)
297 callback_list_add(this_->attr_cbl, cb);
301 bookmarks_store_bookmarks_to_file(struct bookmarks *this_, int limit,int replace) {
303 struct bookmark_item_priv *item,*parent_item;
307 GHashTable *dedup=g_hash_table_new_full(g_str_hash,g_str_equal,g_free,NULL);
309 f=fopen(this_->working_file, replace ? "w+" : "a+");
311 navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file"));
315 this_->bookmarks_list=g_list_first(this_->bookmarks_list);
316 while (this_->bookmarks_list) {
317 item=(struct bookmark_item_priv*)this_->bookmarks_list->data;
320 fullname=g_strdup(item->label);
321 while ((parent_item=parent_item->parent)) {
323 if (parent_item->label) {
324 pathHelper=g_strconcat(parent_item->label,"/",fullname,NULL);
326 fullname=g_strdup(pathHelper);
328 dbg(1,"full name: %s\n",fullname);
332 if (!g_hash_table_lookup(dedup,fullname)) {
333 g_hash_table_insert(dedup,fullname,fullname);
334 if (item->type == type_bookmark) {
335 prostr = projection_to_name(projection_mg);
336 if (fprintf(f,"%s%s%s0x%x %s0x%x type=%s label=\"%s\" path=\"%s\"\n",
337 prostr, *prostr ? ":" : "",
338 item->c.x >= 0 ? "":"-", item->c.x >= 0 ? item->c.x : -item->c.x,
339 item->c.y >= 0 ? "":"-", item->c.y >= 0 ? item->c.y : -item->c.y,
340 "bookmark", item->label,fullname)<1) {
345 if (item->type == type_bookmark_folder) {
346 prostr = projection_to_name(projection_mg);
347 if (fprintf(f,"%s%s%s0x%x %s0x%x type=%s label=\"%s\" path=\"%s\"\n",
348 prostr, *prostr ? ":" : "",
351 "bookmark_folder", item->label,fullname)<1) {
358 /* Limit could be zero, so we start decrementing it from zero and never reach 1
359 or it was bigger and we decreased it earlier. So when this counter becomes 1, we know
360 that we have enough entries in bookmarks file */
366 this_->bookmarks_list=g_list_next(this_->bookmarks_list);
371 g_hash_table_destroy(dedup);
374 map_rect_destroy(this_->mr);
378 unlink(this_->bookmark_file);
379 result=(rename(this_->working_file,this_->bookmark_file)==0);
382 navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file"));
388 * @param create: create the directory where the file is stored
389 * if it does not exist
390 * @return The name of the file used to store destinations with its
391 * full path. Should be freed using g_free.
395 bookmarks_get_destination_file(gboolean create)
397 return g_strjoin(NULL, navit_get_user_data_directory(create), "/destination.txt", NULL);
401 * bookmarks_get_center_file
403 * returns the name of the file used to store the center file with its
406 * arg: gboolean create: create the directory where the file is stored
407 * if it does not exist
410 bookmarks_get_center_file(gboolean create)
412 return g_strjoin(NULL, navit_get_user_data_directory(create), "/center.txt", NULL);
416 bookmarks_set_center_from_file(struct bookmarks *this_, char *file)
421 size_t line_size = 0;
423 struct coord *center;
425 f = fopen(file, "r");
428 getline(&line, &line_size, f);
431 center = transform_center(this_->trans);
432 pro = transform_get_projection(this_->trans);
433 coord_parse(g_strchomp(line), pro, center);
440 bookmarks_write_center_to_file(struct bookmarks *this_, char *file)
444 struct coord *center;
446 f = fopen(file, "w+");
448 center = transform_center(this_->trans);
449 pro = transform_get_projection(this_->trans);
450 coord_print(pro, center, f);
459 bookmarks_emit_dbus_signal(struct bookmarks *this_, struct pcoord *c, const char *description,int create)
461 struct attr attr1,attr2,attr3,attr4,cb,*attr_list[5];
463 attr1.type=attr_type;
464 attr1.u.str="bookmark";
465 attr2.type=attr_data;
466 attr2.u.str=create ? "create" : "delete";
467 attr3.type=attr_data;
468 attr3.u.str=(char *)description;
469 attr4.type=attr_coord;
476 if (navit_get_attr(this_->parent->u.navit, attr_callback_list, &cb, NULL))
477 callback_list_call_attr_4(cb.u.callback_list, attr_command, "dbus_send_signal", attr_list, NULL, &valid);
481 * Record the given set of coordinates as a bookmark
483 * @param navit The navit instance
484 * @param c The coordinate to store
485 * @param description A label which allows the user to later identify this bookmark
489 bookmarks_add_bookmark(struct bookmarks *this_, struct pcoord *pc, const char *description)
491 struct bookmark_item_priv *b_item=g_new0(struct bookmark_item_priv,1);
497 b_item->type=type_bookmark;
499 b_item->type=type_bookmark_folder;
501 b_item->label=g_strdup(description);
502 b_item->parent=this_->current;
503 b_item->children=NULL;
505 this_->current->children=g_list_first(this_->current->children);
506 this_->current->children=g_list_append(this_->current->children,b_item);
507 this_->bookmarks_list=g_list_first(this_->bookmarks_list);
508 this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item);
510 result=bookmarks_store_bookmarks_to_file(this_,0,0);
512 callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
513 bookmarks_clear_hash(this_);
514 bookmarks_load_hash(this_);
516 bookmarks_emit_dbus_signal(this_,&(b_item->c),description,TRUE);
522 bookmarks_cut_bookmark(struct bookmarks *this_, const char *label) {
523 if (bookmarks_copy_bookmark(this_,label)) {
524 return bookmarks_delete_bookmark(this_,label);
530 bookmarks_copy_bookmark(struct bookmarks *this_, const char *label) {
531 bookmarks_item_rewind(this_);
532 if (this_->current->children==NULL) {
535 while (this_->current->iter!=NULL) {
536 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
537 if (!strcmp(data->label,label)) {
538 this_->clipboard->c=data->c;
539 this_->clipboard->type=data->type;
540 this_->clipboard->item=data->item;
541 this_->clipboard->children=data->children;
542 if (!this_->clipboard->label) {
543 g_free(this_->clipboard->label);
545 this_->clipboard->label=g_strdup(data->label);
548 this_->current->iter=g_list_next(this_->current->iter);
553 bookmarks_paste_bookmark(struct bookmarks *this_) {
555 struct bookmark_item_priv* b_item;
557 if (!this_->clipboard->label) {
561 b_item=g_new0(struct bookmark_item_priv,1);
562 b_item->c.x=this_->clipboard->c.x;
563 b_item->c.y=this_->clipboard->c.y;
564 b_item->label=g_strdup(this_->clipboard->label);
565 b_item->type=this_->clipboard->type;
566 b_item->item=this_->clipboard->item;
567 b_item->parent=this_->current;
568 b_item->children=this_->clipboard->children;
570 g_hash_table_insert(this_->bookmarks_hash,b_item->label,b_item);
571 this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item);
572 this_->current->children=g_list_append(this_->current->children,b_item);
573 this_->current->children=g_list_first(this_->current->children);
575 result=bookmarks_store_bookmarks_to_file(this_,0,0);
577 callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
578 bookmarks_clear_hash(this_);
579 bookmarks_load_hash(this_);
586 bookmarks_delete_bookmark(struct bookmarks *this_, const char *label) {
589 bookmarks_item_rewind(this_);
590 if (this_->current->children==NULL) {
593 while (this_->current->iter!=NULL) {
594 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
595 if (!strcmp(data->label,label)) {
596 this_->bookmarks_list=g_list_first(this_->bookmarks_list);
597 this_->bookmarks_list=g_list_remove(this_->bookmarks_list,data);
599 result=bookmarks_store_bookmarks_to_file(this_,0,0);
601 callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
602 bookmarks_clear_hash(this_);
603 bookmarks_load_hash(this_);
605 bookmarks_emit_dbus_signal(this_,&(data->c),label,FALSE);
609 this_->current->iter=g_list_next(this_->current->iter);
616 bookmarks_rename_bookmark(struct bookmarks *this_, const char *oldName, const char* newName) {
619 bookmarks_item_rewind(this_);
620 if (this_->current->children==NULL) {
623 while (this_->current->iter!=NULL) {
624 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
625 if (!strcmp(data->label,oldName)) {
627 data->label=g_strdup(newName);
629 result=bookmarks_store_bookmarks_to_file(this_,0,0);
631 callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
632 bookmarks_clear_hash(this_);
633 bookmarks_load_hash(this_);
637 this_->current->iter=g_list_next(this_->current->iter);
643 struct former_destination{
649 static void free_former_destination(struct former_destination* former_destination){
650 g_free(former_destination->description);
651 g_free(former_destination);
655 static GList* read_former_destination_map_as_list(struct map *map){
659 struct former_destination *dest;
661 if (map && (mr=map_rect_new(map, NULL))) {
662 while ((item=map_rect_get_item(mr))) {
663 if (item->type != type_former_destination) continue;
664 dest = g_new(struct former_destination, 1);
665 dest->type=item->type;
666 item_attr_get(item, attr_label, &attr);
667 dest->description = g_strdup(attr.u.str);
668 item_coord_get(item, &(dest->c), 1);
669 list = g_list_prepend(list, dest);
671 map_rect_destroy(mr);
673 return g_list_reverse(list);
677 destination_equal(struct former_destination* dest1, struct former_destination* dest2)
679 if ((dest1->type == dest2->type) &&
680 (!strcmp(dest1->description, dest2->description)) &&
681 (coord_equal(&(dest1->c), &(dest2->c)))){
688 remove_destination_from_list(struct former_destination* dest_to_remove, GList* former_destinations)
690 GList* curr_el = former_destinations;
691 GList* prev_el = NULL;
692 struct former_destination* curr_dest;
695 curr_dest = curr_el->data;
696 if (destination_equal(dest_to_remove, curr_dest)){
697 free_former_destination(curr_dest);
698 curr_el = g_list_remove(curr_el, curr_dest);
701 curr_el = g_list_next(curr_el);
704 return g_list_first(prev_el);
708 write_former_destinations(GList* former_destinations, char *former_destination_file, enum projection proj)
711 GList* currdest = NULL;
712 struct former_destination *dest;
713 const char* prostr = projection_to_name(proj);
714 f=fopen(former_destination_file, "w");
716 for(currdest = former_destinations; currdest; currdest = g_list_next(currdest)){
717 dest = currdest->data;
718 if (dest->description)
719 fprintf(f,"type=%s label=\"%s\"\n", item_to_name(dest->type), dest->description);
721 fprintf(f,"type=%s\n", item_to_name(dest->type));
722 fprintf(f,"%s%s%s0x%x %s0x%x\n",
723 prostr, *prostr ? ":" : "",
724 dest->c.x >= 0 ? "":"-", dest->c.x >= 0 ? dest->c.x : -dest->c.x,
725 dest->c.y >= 0 ? "":"-", dest->c.y >= 0 ? dest->c.y : -dest->c.y);
730 dbg(0, "Error updating destinations file %s: %s\n", former_destination_file, strerror(errno));
735 * @param limit Limits the number of entries in the "backlog". Set to 0 for "infinite"
738 bookmarks_append_coord(struct map *former_destination_map, char *former_destination_file,
739 struct pcoord *c, enum item_type type, const char *description, int limit)
741 struct former_destination *new_dest;
742 GList* former_destinations = NULL;
743 GList* former_destinations_shortened = NULL;
744 int no_of_former_destinations;
746 former_destinations = read_former_destination_map_as_list(former_destination_map);
748 new_dest = g_new(struct former_destination, 1);
749 new_dest->type = type;
750 new_dest->description = g_strdup(description?description:_("Map Point"));
751 new_dest->c.x = c->x;
752 new_dest->c.y = c->y;
753 former_destinations = remove_destination_from_list(new_dest, former_destinations);
754 former_destinations = g_list_append(former_destinations, new_dest);
756 no_of_former_destinations = g_list_length(former_destinations);
757 if (limit > 0 && no_of_former_destinations > limit)
758 former_destinations_shortened = g_list_nth(former_destinations, no_of_former_destinations - limit);
760 former_destinations_shortened = former_destinations;
762 write_former_destinations(former_destinations_shortened, former_destination_file, map_projection(former_destination_map));
763 g_list_foreach(former_destinations, (GFunc) free_former_destination, NULL);
764 g_list_free(former_destinations);