/** * Navit, a modular navigation system. * Copyright (C) 2005-2010 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 "config.h" #ifdef HAVE_UNISTD_H #include #endif #include "file.h" #include "debug.h" #include "projection.h" #include "coord.h" #include "transform.h" #include "callback.h" #include "map.h" #include "command.h" #include "bookmarks.h" #include "navit.h" #include "navit_nls.h" #include "util.h" /* FIXME: Move this to support directory */ #ifdef _MSC_VER #include static int ftruncate(int fd, __int64 length) { HANDLE fh = (HANDLE)_get_osfhandle(fd); if (!fh || _lseeki64(fd, length, SEEK_SET)) { return -1; } return SetEndOfFile(fh) ? 0 : -1; } #endif /* _MSC_VER */ struct bookmarks { //data storage struct map *bookmark; struct map_rect *mr; GHashTable *bookmarks_hash; GList *bookmarks_list; char* bookmark_file; char *working_file; struct bookmark_item_priv* clipboard; struct bookmark_item_priv* root; struct bookmark_item_priv* current; //Refs to other objects struct transformation *trans; struct attr **attrs; struct callback_list *attr_cbl; struct attr *parent; }; struct bookmark_item_priv { char *label; enum item_type type; struct pcoord c; GList *children; GList *iter; struct bookmark_item_priv *parent; struct item item; }; void bookmarks_move_root(struct bookmarks *this_) { this_->current=this_->root; this_->current->iter=g_list_first(this_->current->children); dbg(lvl_info,"Root list have %u entries\n",g_list_length(this_->current->children)); return; } void bookmarks_move_up(struct bookmarks *this_) { if (this_->current->parent) { this_->current=this_->current->parent; this_->current->iter=this_->current->children; } return; } int bookmarks_move_down(struct bookmarks *this_,const char* name) { bookmarks_item_rewind(this_); if (this_->current->children==NULL) { return 0; } while (this_->current->iter!=NULL) { struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data; if (!strcmp(data->label,name)) { this_->current=(struct bookmark_item_priv*)this_->current->iter->data; this_->current->iter=g_list_first(this_->current->children); dbg(lvl_info,"%s list have %u entries\n",this_->current->label,g_list_length(this_->current->children)); return 1; } this_->current->iter=g_list_next(this_->current->iter); } return 0; } void bookmarks_item_rewind(struct bookmarks* this_) { this_->current->children=g_list_first(this_->current->children); this_->current->iter=this_->current->children; this_->current->iter=this_->current->children; } struct item* bookmarks_get_item(struct bookmarks* this_) { struct item item,*ret; if (this_->current->iter==NULL) { return NULL; } item=((struct bookmark_item_priv*)this_->current->iter->data)->item; this_->current->iter=g_list_next(this_->current->iter); ret = map_rect_get_item_byid(this_->mr, item.id_hi, item.id_lo); return ret; } int bookmarks_get_bookmark_count(struct bookmarks* this_) { int ret=0; bookmarks_item_rewind(this_); while (this_->current->iter!=NULL) { struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data; if (data->type == type_bookmark) { ret++; } this_->current->iter=g_list_next(this_->current->iter); } return ret; } const char* bookmarks_item_cwd(struct bookmarks* this_) { return this_->current->label; } static void bookmarks_clear_item(struct bookmark_item_priv *b_item) { b_item->children=g_list_first(b_item->children); while(b_item->children) { bookmarks_clear_item((struct bookmark_item_priv*)b_item->children->data); b_item->children=g_list_next(b_item->children); } g_free(b_item->label); g_free(b_item); } static void bookmarks_clear_hash(struct bookmarks *this_) { if (this_->mr) { map_rect_destroy(this_->mr); } bookmarks_clear_item(this_->root); g_hash_table_destroy(this_->bookmarks_hash); g_list_free(this_->bookmarks_list); } static void bookmarks_load_hash(struct bookmarks *this_) { struct bookmark_item_priv *b_item; struct item *item; struct attr attr; struct coord c; char *pos,*finder; char *copy_helper; if (this_->mr) { map_rect_destroy(this_->mr); } this_->mr=map_rect_new(this_->bookmark, NULL); this_->bookmarks_hash=g_hash_table_new(g_str_hash, g_str_equal); this_->root=g_new0(struct bookmark_item_priv,1); this_->root->type=type_none; this_->root->parent=NULL; this_->root->children=NULL; bookmarks_move_root(this_); while ((item=map_rect_get_item(this_->mr))) { if (item->type != type_bookmark && item->type != type_bookmark_folder ) continue; if (!item_attr_get(item, attr_path, &attr)) { item_attr_get(item, attr_label, &attr); } item_coord_get(item, &c, 1); b_item=g_new0(struct bookmark_item_priv,1); b_item->c.x=c.x; b_item->c.y=c.y; b_item->label=g_strdup(attr.u.str); b_item->type=item->type; b_item->item=*item; //Prepare position bookmarks_move_root(this_); finder=b_item->label; while ((pos=strchr(finder,'/'))) { *pos=0x00; dbg(lvl_debug,"Found path entry: %s\n",finder); if (!bookmarks_move_down(this_,finder)) { struct bookmark_item_priv *path_item=g_new0(struct bookmark_item_priv,1); path_item->type=type_bookmark_folder; path_item->parent=this_->current; path_item->children=NULL; path_item->label=g_strdup(finder); this_->current->children=g_list_append(this_->current->children,path_item); this_->current=path_item; g_hash_table_insert(this_->bookmarks_hash,b_item->label,path_item); this_->bookmarks_list=g_list_append(this_->bookmarks_list,path_item); } finder+=strlen(finder)+1; } copy_helper=g_strdup(finder); free(b_item->label); b_item->label=copy_helper; b_item->parent=this_->current; g_hash_table_insert(this_->bookmarks_hash,b_item->label,b_item); this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item); this_->current->children=g_list_append(this_->current->children,b_item); this_->current->children=g_list_first(this_->current->children); dbg(lvl_debug,"Added %s to %s and current list now %u long\n",b_item->label,this_->current->label,g_list_length(this_->current->children)); } bookmarks_move_root(this_); } struct bookmarks * bookmarks_new(struct attr *parent, struct attr **attrs, struct transformation *trans) { struct bookmarks *this_; if (parent->type!=attr_navit) { return NULL; } this_ = g_new0(struct bookmarks,1); this_->attr_cbl=callback_list_new(); this_->parent=parent; //this_->attrs=attr_list_dup(attrs); this_->trans=trans; this_->bookmark_file=g_strjoin(NULL, navit_get_user_data_directory(TRUE), "/bookmark.txt", NULL); this_->working_file=g_strjoin(NULL, navit_get_user_data_directory(TRUE), "/bookmark.txt.tmp", NULL); this_->clipboard=g_new0(struct bookmark_item_priv,1); { //Load map now struct attr type={attr_type, {"textfile"}}, data={attr_data, {this_->bookmark_file}}, no_warn={attr_no_warning_if_map_file_missing, {(void *)1}}; struct attr *attrs[]={&type, &data, &no_warn, NULL}; this_->bookmark=map_new(this_->parent, attrs); if (!this_->bookmark) return NULL; bookmarks_load_hash(this_); } return this_; } void bookmarks_destroy(struct bookmarks *this_) { bookmarks_clear_hash(this_); map_destroy(this_->bookmark); callback_list_destroy(this_->attr_cbl); g_free(this_->bookmark_file); g_free(this_->working_file); g_free(this_->clipboard); g_free(this_); } struct map* bookmarks_get_map(struct bookmarks *this_) { return this_->bookmark; } enum projection bookmarks_get_projection(struct bookmarks *this_){ return map_projection(this_->bookmark); } void bookmarks_add_callback(struct bookmarks *this_, struct callback *cb) { callback_list_add(this_->attr_cbl, cb); } static int bookmarks_store_bookmarks_to_file(struct bookmarks *this_, int limit,int replace) { FILE *f; struct bookmark_item_priv *item,*parent_item; char *fullname; const char *prostr; int result; GHashTable *dedup=g_hash_table_new_full(g_str_hash,g_str_equal,g_free,NULL); f=fopen(this_->working_file, replace ? "w+" : "a+"); if (f==NULL) { navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file")); return FALSE; } this_->bookmarks_list=g_list_first(this_->bookmarks_list); while (this_->bookmarks_list) { item=(struct bookmark_item_priv*)this_->bookmarks_list->data; parent_item=item; fullname=g_strdup(item->label); while ((parent_item=parent_item->parent)) { char *pathHelper; if (parent_item->label) { pathHelper=g_strconcat(parent_item->label,"/",fullname,NULL); g_free(fullname); fullname=g_strdup(pathHelper); g_free(pathHelper); dbg(lvl_debug,"full name: %s\n",fullname); } } if (!g_hash_table_lookup(dedup,fullname)) { g_hash_table_insert(dedup,fullname,fullname); if (item->type == type_bookmark) { prostr = projection_to_name(projection_mg); if (fprintf(f,"%s%s%s0x%x %s0x%x type=%s label=\"%s\" path=\"%s\"\n", prostr, *prostr ? ":" : "", item->c.x >= 0 ? "":"-", item->c.x >= 0 ? item->c.x : -item->c.x, item->c.y >= 0 ? "":"-", item->c.y >= 0 ? item->c.y : -item->c.y, "bookmark", item->label,fullname)<1) { g_free(fullname); break; } } if (item->type == type_bookmark_folder) { prostr = projection_to_name(projection_mg); if (fprintf(f,"%s%s%s0x%x %s0x%x type=%s label=\"%s\" path=\"%s\"\n", prostr, *prostr ? ":" : "", "", 0, "", 0, "bookmark_folder", item->label,fullname)<1) { g_free(fullname); break; } } } /* Limit could be zero, so we start decrementing it from zero and never reach 1 or it was bigger and we decreased it earlier. So when this counter becomes 1, we know that we have enough entries in bookmarks file */ if (limit==1) { break; } limit--; this_->bookmarks_list=g_list_next(this_->bookmarks_list); } fclose(f); g_hash_table_destroy(dedup); if (this_->mr) { map_rect_destroy(this_->mr); this_->mr = 0; } unlink(this_->bookmark_file); result=(rename(this_->working_file,this_->bookmark_file)==0); if (!result) { navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file")); } return result; } /** * @param create: create the directory where the file is stored * if it does not exist * @return The name of the file used to store destinations with its * full path. Should be freed using g_free. * */ char* bookmarks_get_destination_file(gboolean create) { return g_strjoin(NULL, navit_get_user_data_directory(create), "/destination.txt", NULL); } /* * bookmarks_get_center_file * * returns the name of the file used to store the center file with its * full path * * arg: gboolean create: create the directory where the file is stored * if it does not exist */ char* bookmarks_get_center_file(gboolean create) { return g_strjoin(NULL, navit_get_user_data_directory(create), "/center.txt", NULL); } void bookmarks_set_center_from_file(struct bookmarks *this_, char *file) { FILE *f; char *line = NULL; size_t line_size = 0; enum projection pro; struct coord *center; f = fopen(file, "r"); if (! f) return; getline(&line, &line_size, f); fclose(f); if (line) { center = transform_center(this_->trans); pro = transform_get_projection(this_->trans); coord_parse(g_strchomp(line), pro, center); free(line); } return; } void bookmarks_write_center_to_file(struct bookmarks *this_, char *file) { FILE *f; enum projection pro; struct coord *center; f = fopen(file, "w+"); if (f) { center = transform_center(this_->trans); pro = transform_get_projection(this_->trans); coord_print(pro, center, f); fclose(f); } else { perror(file); } return; } static void bookmarks_emit_dbus_signal(struct bookmarks *this_, struct pcoord *c, const char *description,int create) { struct attr attr1,attr2,attr3,attr4,cb,*attr_list[5]; int valid=0; attr1.type=attr_type; attr1.u.str="bookmark"; attr2.type=attr_data; attr2.u.str=create ? "create" : "delete"; attr3.type=attr_data; attr3.u.str=(char *)description; attr4.type=attr_coord; attr4.u.pcoord=c; attr_list[0]=&attr1; attr_list[1]=&attr2; attr_list[2]=&attr3; attr_list[3]=&attr4; attr_list[4]=NULL; if (navit_get_attr(this_->parent->u.navit, attr_callback_list, &cb, NULL)) callback_list_call_attr_4(cb.u.callback_list, attr_command, "dbus_send_signal", attr_list, NULL, &valid); } /** * Record the given set of coordinates as a bookmark * * @param navit The navit instance * @param c The coordinate to store * @param description A label which allows the user to later identify this bookmark * @returns nothing */ int bookmarks_add_bookmark(struct bookmarks *this_, struct pcoord *pc, const char *description) { struct bookmark_item_priv *b_item=g_new0(struct bookmark_item_priv,1); int result; if (pc) { b_item->c.x=pc->x; b_item->c.y=pc->y; b_item->type=type_bookmark; } else { b_item->type=type_bookmark_folder; } b_item->label=g_strdup(description); b_item->parent=this_->current; b_item->children=NULL; this_->current->children=g_list_first(this_->current->children); this_->current->children=g_list_append(this_->current->children,b_item); this_->bookmarks_list=g_list_first(this_->bookmarks_list); this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item); result=bookmarks_store_bookmarks_to_file(this_,0,0); callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map); bookmarks_clear_hash(this_); bookmarks_load_hash(this_); bookmarks_emit_dbus_signal(this_,&(b_item->c),description,TRUE); return result; } int bookmarks_cut_bookmark(struct bookmarks *this_, const char *label) { if (bookmarks_copy_bookmark(this_,label)) { return bookmarks_delete_bookmark(this_,label); } return FALSE; } int bookmarks_copy_bookmark(struct bookmarks *this_, const char *label) { bookmarks_item_rewind(this_); if (this_->current->children==NULL) { return 0; } while (this_->current->iter!=NULL) { struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data; if (!strcmp(data->label,label)) { this_->clipboard->c=data->c; this_->clipboard->type=data->type; this_->clipboard->item=data->item; this_->clipboard->children=data->children; if (!this_->clipboard->label) { g_free(this_->clipboard->label); } this_->clipboard->label=g_strdup(data->label); return TRUE; } this_->current->iter=g_list_next(this_->current->iter); } return FALSE; } int bookmarks_paste_bookmark(struct bookmarks *this_) { int result; struct bookmark_item_priv* b_item; if (!this_->clipboard->label) { return FALSE; } b_item=g_new0(struct bookmark_item_priv,1); b_item->c.x=this_->clipboard->c.x; b_item->c.y=this_->clipboard->c.y; b_item->label=g_strdup(this_->clipboard->label); b_item->type=this_->clipboard->type; b_item->item=this_->clipboard->item; b_item->parent=this_->current; b_item->children=this_->clipboard->children; g_hash_table_insert(this_->bookmarks_hash,b_item->label,b_item); this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item); this_->current->children=g_list_append(this_->current->children,b_item); this_->current->children=g_list_first(this_->current->children); result=bookmarks_store_bookmarks_to_file(this_,0,0); callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map); bookmarks_clear_hash(this_); bookmarks_load_hash(this_); return result; } int bookmarks_delete_bookmark(struct bookmarks *this_, const char *label) { int result; bookmarks_item_rewind(this_); if (this_->current->children==NULL) { return 0; } while (this_->current->iter!=NULL) { struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data; if (!strcmp(data->label,label)) { this_->bookmarks_list=g_list_first(this_->bookmarks_list); this_->bookmarks_list=g_list_remove(this_->bookmarks_list,data); result=bookmarks_store_bookmarks_to_file(this_,0,0); callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map); bookmarks_clear_hash(this_); bookmarks_load_hash(this_); bookmarks_emit_dbus_signal(this_,&(data->c),label,FALSE); return result; } this_->current->iter=g_list_next(this_->current->iter); } return FALSE; } int bookmarks_rename_bookmark(struct bookmarks *this_, const char *oldName, const char* newName) { int result; bookmarks_item_rewind(this_); if (this_->current->children==NULL) { return 0; } while (this_->current->iter!=NULL) { struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data; if (!strcmp(data->label,oldName)) { g_free(data->label); data->label=g_strdup(newName); result=bookmarks_store_bookmarks_to_file(this_,0,0); callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map); bookmarks_clear_hash(this_); bookmarks_load_hash(this_); return result; } this_->current->iter=g_list_next(this_->current->iter); } return FALSE; } struct former_destination{ enum item_type type; char* description; GList* c; }; static void free_former_destination(struct former_destination* former_destination){ g_free(former_destination->description); g_list_foreach(former_destination->c, (GFunc)g_free, NULL); g_list_free(former_destination->c); g_free(former_destination); } /* * Doesn't read any items besides former_destination. So active waypoints items of type former_itinerary and former_itinerary_part are skipped. */ static GList* read_former_destination_map_as_list(struct map *map){ struct map_rect *mr; struct item *item; struct attr attr; struct former_destination *dest; struct coord c; int more; GList* list = NULL; if (map && (mr=map_rect_new(map, NULL))) { while ((item=map_rect_get_item(mr))) { if (item->type != type_former_destination) continue; dest = g_new0(struct former_destination, 1); dest->type=item->type; item_attr_get(item, attr_label, &attr); dest->description = g_strdup(attr.u.str); more = item_coord_get(item, &c, 1); while (more) { dest->c = g_list_append(dest->c, g_new(struct coord, 1)); *(struct coord *)g_list_last(dest->c)->data = c; more = item_coord_get(item, &c, 1); } list = g_list_prepend(list, dest); } map_rect_destroy(mr); } return g_list_reverse(list); } static int destination_equal(struct former_destination* dest1, struct former_destination* dest2, int ignore_descriptions) { if ((dest1->type == dest2->type) && (ignore_descriptions || !strcmp(dest1->description, dest2->description)) && (coord_equal((struct coord *)g_list_last(dest1->c)->data, (struct coord *)g_list_last(dest2->c)->data))){ return TRUE; } return FALSE; } /* * Find destination in given GList. If remove_found is non-zero, any matching items are removed and new beginning of the list is returned. * If remove_found is zero, last matching item is returned. In the latter case, description is ignored and can be NULL. */ static GList* find_destination_in_list(struct former_destination* dest_to_remove, GList* former_destinations, int remove_found) { GList* curr_el = former_destinations; GList* prev_el = NULL; GList* found_el = NULL; struct former_destination* curr_dest; while(curr_el){ curr_dest = curr_el->data; if (destination_equal(dest_to_remove, curr_dest, remove_found?0:1)) { if(remove_found) { free_former_destination(curr_dest); curr_el = g_list_remove(curr_el, curr_dest); continue; } else { found_el=curr_el; } } prev_el = curr_el; curr_el = g_list_next(curr_el); } if(remove_found) return g_list_first(prev_el); else return found_el; } static void write_former_destinations(GList* former_destinations, char *former_destination_file, enum projection proj) { FILE *f; GList* currdest = NULL; GList* c_list = NULL; struct coord *c; struct former_destination *dest; const char* prostr = projection_to_name(proj); f=fopen(former_destination_file, "w"); if (f) { for(currdest = former_destinations; currdest; currdest = g_list_next(currdest)){ dest = currdest->data; if (dest->description) fprintf(f,"type=%s label=\"%s\"\n", item_to_name(dest->type), dest->description); else fprintf(f,"type=%s\n", item_to_name(dest->type)); c_list = dest->c; do { c = (struct coord *)c_list->data; fprintf(f,"%s%s%s0x%x %s0x%x\n", prostr, *prostr ? ":" : "", c->x >= 0 ? "":"-", c->x >= 0 ? c->x : -c->x, c->y >= 0 ? "":"-", c->y >= 0 ? c->y : -c->y); c_list = g_list_next(c_list); } while (c_list); } fclose(f); } else { dbg(lvl_error, "Error updating destinations file %s: %s\n", former_destination_file, strerror(errno)); } } /** * Append recent destination(s) item to the former destionations map. * @param former_destination_map * @param former_destination_file * @param c coordinates of item point(s). Can be set to NULL when navigation is stopped to remove type_former_itinerary and * type_former_itinerary_part items from the file. * @param count number of points in this item. Set to 0 when navigation is stopped. * @param type type_former_destination, type_former_itinerary and type_former_itinerary_part are meaningful here * @param description character string used to identify this destination. If NULL, most recent waypoint at these coordinates will be used * to get description. * @param limit Limits the number of entries in the "backlog". Set to 0 for "infinite" */ void bookmarks_append_destinations(struct map *former_destination_map, char *former_destination_file, struct pcoord *c, int count, enum item_type type, const char *description, int limit) { struct former_destination *new_dest=NULL; GList* former_destinations = NULL; GList* former_destinations_shortened = NULL; struct coord* c_dup; int no_of_former_destinations; int i; former_destinations = read_former_destination_map_as_list(former_destination_map); if(c && count>0) { GList *older; struct coord ctmp; new_dest = g_new0(struct former_destination, 1); new_dest->type = type; for (i=0; ic = g_list_append(new_dest->c, c_dup); } older=find_destination_in_list(new_dest, former_destinations,0); if(!description && older) description=((struct former_destination *)older->data)->description; new_dest->description = g_strdup(description?description:_("Map Point")); former_destinations = find_destination_in_list(new_dest, former_destinations, 1); former_destinations = g_list_append(former_destinations, new_dest); } no_of_former_destinations = g_list_length(former_destinations); if (limit > 0 && no_of_former_destinations > limit) former_destinations_shortened = g_list_nth(former_destinations, no_of_former_destinations - limit); else former_destinations_shortened = former_destinations; write_former_destinations(former_destinations_shortened, former_destination_file, map_projection(former_destination_map)); g_list_foreach(former_destinations, (GFunc) free_former_destination, NULL); g_list_free(former_destinations); }