Fix:core:Add comments and some #defines for constants
[profile/ivi/navit.git] / navit / navit / bookmarks.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2010 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 <stdlib.h>
21 #include <glib.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include "config.h"
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include "file.h"
29 #include "debug.h"
30 #include "projection.h"
31 #include "coord.h"
32 #include "transform.h"
33 #include "callback.h"
34 #include "map.h"
35 #include "command.h"
36 #include "bookmarks.h"
37 #include "navit.h"
38 #include "navit_nls.h"
39 #include "util.h"
40
41 /* FIXME: Move this to support directory */
42 #ifdef _MSC_VER
43 #include <windows.h>
44 static int ftruncate(int fd, __int64 length)
45 {
46         HANDLE fh = (HANDLE)_get_osfhandle(fd);
47         if (!fh || _lseeki64(fd, length, SEEK_SET)) {
48                 return -1;
49         }
50         return SetEndOfFile(fh) ? 0 : -1;
51 }
52 #endif /* _MSC_VER */
53
54 struct bookmarks {
55         //data storage
56         struct map *bookmark;
57         struct map_rect *mr;
58         GHashTable *bookmarks_hash;
59         GList *bookmarks_list;
60         char* bookmark_file;
61         char *working_file;
62         struct bookmark_item_priv* clipboard;
63         struct bookmark_item_priv* root;
64         struct bookmark_item_priv* current;
65
66         //Refs to other objects
67         struct transformation *trans;
68         struct attr **attrs;
69         struct callback_list *attr_cbl;
70         struct attr *parent;    
71 };
72
73 struct bookmark_item_priv {
74         char *label;
75         enum item_type type;
76         struct pcoord c;
77         GList *children;
78         GList *iter;
79         struct bookmark_item_priv *parent;
80         struct item item;
81 };
82
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));
87         return;
88 }
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;
93         }
94         return;
95 }
96 int bookmarks_move_down(struct bookmarks *this_,const char* name) {
97         bookmarks_item_rewind(this_);
98         if (this_->current->children==NULL) {
99                 return 0;
100         }
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));
107                         return 1;
108                 }
109                 this_->current->iter=g_list_next(this_->current->iter);
110         }
111         return 0;
112 }
113
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;
118 }
119 struct item* bookmarks_get_item(struct bookmarks* this_) {
120         struct item item,*ret;
121     if (this_->current->iter==NULL) {
122                 return NULL;
123         }
124
125         item=((struct bookmark_item_priv*)this_->current->iter->data)->item;
126         this_->current->iter=g_list_next(this_->current->iter);
127
128         ret = map_rect_get_item_byid(this_->mr, item.id_hi, item.id_lo);
129
130         return ret;
131 }
132
133 const char* bookmarks_item_cwd(struct bookmarks* this_) {
134         return this_->current->label;
135 }
136
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);
142         }
143         g_free(b_item->label);
144         g_free(b_item);
145 }
146
147 static void
148 bookmarks_clear_hash(struct bookmarks *this_) {
149         if (this_->mr) {
150             map_rect_destroy(this_->mr);
151         }
152         bookmarks_clear_item(this_->root);
153         g_hash_table_destroy(this_->bookmarks_hash);
154         g_list_free(this_->bookmarks_list);
155 }
156
157 static void
158 bookmarks_load_hash(struct bookmarks *this_) {
159         struct bookmark_item_priv *b_item;
160         struct item *item;
161         struct attr attr;
162         struct coord c;
163         char *pos,*finder;
164         char *copy_helper;
165
166         if (this_->mr) {
167                 map_rect_destroy(this_->mr);
168         }
169         this_->mr=map_rect_new(this_->bookmark, NULL);
170
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_);
177
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);
182                 }
183                 item_coord_get(item, &c, 1);
184
185                 b_item=g_new0(struct bookmark_item_priv,1);
186                 b_item->c.x=c.x;
187                 b_item->c.y=c.y;
188                 b_item->label=g_strdup(attr.u.str);
189                 b_item->type=item->type;
190                 b_item->item=*item;
191
192                 //Prepare position
193                 bookmarks_move_root(this_);
194                 finder=b_item->label;
195                 while ((pos=strchr(finder,'/'))) {
196                         *pos=0x00;
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);
204
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);
209                         }
210                         finder+=strlen(finder)+1;
211                 }
212                 copy_helper=g_strdup(finder);
213                 free(b_item->label);
214                 b_item->label=copy_helper;
215                 b_item->parent=this_->current;
216
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));
222         }
223         bookmarks_move_root(this_);
224 }
225
226 struct bookmarks *
227 bookmarks_new(struct attr *parent, struct attr **attrs, struct transformation *trans) {
228         struct bookmarks *this_;
229
230         if (parent->type!=attr_navit) {
231                 return NULL;
232         }
233
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);
238         this_->trans=trans;
239
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);
242
243         this_->clipboard=g_new0(struct bookmark_item_priv,1);
244
245         {
246                 //Load map now
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)
251                         return NULL;
252                 bookmarks_load_hash(this_);
253         }
254
255         return this_;
256 }
257
258 void
259 bookmarks_destroy(struct bookmarks *this_) {
260
261         bookmarks_clear_hash(this_);
262
263         map_destroy(this_->bookmark);
264         callback_list_destroy(this_->attr_cbl);
265
266         g_free(this_->bookmark_file);
267         g_free(this_->working_file);
268
269         g_free(this_->clipboard);
270         g_free(this_);
271 }
272
273 struct map*
274 bookmarks_get_map(struct bookmarks *this_) {
275         return this_->bookmark;
276 }
277
278 enum projection bookmarks_get_projection(struct bookmarks *this_){
279         return map_projection(this_->bookmark);
280 }
281 void
282 bookmarks_add_callback(struct bookmarks *this_, struct callback *cb)
283 {
284         callback_list_add(this_->attr_cbl, cb);
285 }
286
287 static int
288 bookmarks_store_bookmarks_to_file(struct bookmarks *this_,  int limit,int replace) {
289         FILE *f;
290         struct bookmark_item_priv *item,*parent_item;
291         char *fullname;
292         const char *prostr;
293         int result;
294         GHashTable *dedup=g_hash_table_new_full(g_str_hash,g_str_equal,g_free,NULL);
295
296         f=fopen(this_->working_file, replace ? "w+" : "a+");
297         if (f==NULL) {
298                 navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file"));
299                 return FALSE;
300         }
301
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;
305
306                 parent_item=item;
307                 fullname=g_strdup(item->label);
308                 while ((parent_item=parent_item->parent)) {
309                         char *pathHelper;
310                         if (parent_item->label) {
311                                 pathHelper=g_strconcat(parent_item->label,"/",fullname,NULL);
312                                 g_free(fullname);
313                                 fullname=g_strdup(pathHelper);
314                                 g_free(pathHelper);
315                                 dbg(1,"full name: %s\n",fullname);
316                         }
317                 }
318
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) {
328                                         g_free(fullname);
329                                         break;
330                                 }
331                         }
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 ? ":" : "",
336                                          "", 0,
337                                          "", 0,
338                                          "bookmark_folder", item->label,fullname)<1) {
339                                         g_free(fullname);
340                                         break;
341                                 }
342                         }
343                 }
344
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 */
348                 if (limit==1) {
349                         break;
350                 }
351                 limit--;
352
353                 this_->bookmarks_list=g_list_next(this_->bookmarks_list);
354         }
355
356         fclose(f);
357
358     g_hash_table_destroy(dedup);
359
360     if (this_->mr) {
361         map_rect_destroy(this_->mr);
362         this_->mr = 0;
363     }
364
365     unlink(this_->bookmark_file);
366         result=(rename(this_->working_file,this_->bookmark_file)==0);
367         if (!result) 
368         {
369                 navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file"));
370         }
371         return result;
372 }
373
374 /**
375  * @param create: create the directory where the file is stored
376  * if it does not exist
377  * @return The name of the file used to store destinations with its
378  * full path. Should be freed using g_free.
379  *
380  */
381 char*
382 bookmarks_get_destination_file(gboolean create)
383 {
384         return g_strjoin(NULL, navit_get_user_data_directory(create), "/destination.txt", NULL);
385 }
386
387 /*
388  * bookmarks_get_center_file
389  *
390  * returns the name of the file used to store the center file  with its
391  * full path
392  *
393  * arg: gboolean create: create the directory where the file is stored
394  * if it does not exist
395  */
396 char*
397 bookmarks_get_center_file(gboolean create)
398 {
399         return g_strjoin(NULL, navit_get_user_data_directory(create), "/center.txt", NULL);
400 }
401
402 void
403 bookmarks_set_center_from_file(struct bookmarks *this_, char *file)
404 {
405         FILE *f;
406         char *line = NULL;
407
408         size_t line_size = 0;
409         enum projection pro;
410         struct coord *center;
411
412         f = fopen(file, "r");
413         if (! f)
414                 return;
415         getline(&line, &line_size, f);
416         fclose(f);
417         if (line) {
418                 center = transform_center(this_->trans);
419                 pro = transform_get_projection(this_->trans);
420                 coord_parse(g_strchomp(line), pro, center);
421                 free(line);
422         }
423         return;
424 }
425
426 void
427 bookmarks_write_center_to_file(struct bookmarks *this_, char *file)
428 {
429         FILE *f;
430         enum projection pro;
431         struct coord *center;
432
433         f = fopen(file, "w+");
434         if (f) {
435                 center = transform_center(this_->trans);
436                 pro = transform_get_projection(this_->trans);
437                 coord_print(pro, center, f);
438                 fclose(f);
439         } else {
440                 perror(file);
441         }
442         return;
443 }
444
445 static void
446 bookmarks_emit_dbus_signal(struct bookmarks *this_, struct pcoord *c, const char *description,int create)
447 {
448     struct attr attr1,attr2,attr3,attr4,cb,*attr_list[5];
449         int valid=0;
450         attr1.type=attr_type;
451         attr1.u.str="bookmark";
452     attr2.type=attr_data;
453     attr2.u.str=create ? "create" : "delete";
454         attr3.type=attr_data;
455         attr3.u.str=(char *)description;
456     attr4.type=attr_coord;
457     attr4.u.pcoord=c;
458         attr_list[0]=&attr1;
459         attr_list[1]=&attr2;
460     attr_list[2]=&attr3;
461     attr_list[3]=&attr4;
462         attr_list[4]=NULL;
463         if (navit_get_attr(this_->parent->u.navit, attr_callback_list, &cb, NULL))
464                 callback_list_call_attr_4(cb.u.callback_list, attr_command, "dbus_send_signal", attr_list, NULL, &valid);
465 }
466
467 /**
468  * Record the given set of coordinates as a bookmark
469  *
470  * @param navit The navit instance
471  * @param c The coordinate to store
472  * @param description A label which allows the user to later identify this bookmark
473  * @returns nothing
474  */
475 int
476 bookmarks_add_bookmark(struct bookmarks *this_, struct pcoord *pc, const char *description)
477 {
478         struct bookmark_item_priv *b_item=g_new0(struct bookmark_item_priv,1);
479         int result;
480
481         if (pc) {
482                 b_item->c.x=pc->x;
483                 b_item->c.y=pc->y;
484                 b_item->type=type_bookmark;
485         } else {
486                 b_item->type=type_bookmark_folder;
487         }
488         b_item->label=g_strdup(description);
489         b_item->parent=this_->current;
490         b_item->children=NULL;
491
492         this_->current->children=g_list_first(this_->current->children);
493         this_->current->children=g_list_append(this_->current->children,b_item);
494         this_->bookmarks_list=g_list_first(this_->bookmarks_list);
495         this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item);
496
497         result=bookmarks_store_bookmarks_to_file(this_,0,0);
498
499         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
500         bookmarks_clear_hash(this_);
501         bookmarks_load_hash(this_);
502
503     bookmarks_emit_dbus_signal(this_,&(b_item->c),description,TRUE);
504
505         return result;
506 }
507
508 int
509 bookmarks_cut_bookmark(struct bookmarks *this_, const char *label) {
510         if (bookmarks_copy_bookmark(this_,label)) {
511                 return bookmarks_delete_bookmark(this_,label);
512         }
513
514         return FALSE;
515 }
516 int
517 bookmarks_copy_bookmark(struct bookmarks *this_, const char *label) {
518         bookmarks_item_rewind(this_);
519         if (this_->current->children==NULL) {
520                 return 0;
521         }
522         while (this_->current->iter!=NULL) {
523                 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
524                 if (!strcmp(data->label,label)) {
525                         this_->clipboard->c=data->c;
526                         this_->clipboard->type=data->type;
527                         this_->clipboard->item=data->item;
528                         this_->clipboard->children=data->children;
529                         if (!this_->clipboard->label) {
530                                 g_free(this_->clipboard->label);
531                         }
532                         this_->clipboard->label=g_strdup(data->label);
533                         return TRUE;
534                 }
535                 this_->current->iter=g_list_next(this_->current->iter);
536         }
537         return FALSE;
538 }
539 int
540 bookmarks_paste_bookmark(struct bookmarks *this_) {
541         int result;
542         struct bookmark_item_priv* b_item;
543
544         if (!this_->clipboard->label) {
545             return FALSE;
546     }
547
548         b_item=g_new0(struct bookmark_item_priv,1);
549         b_item->c.x=this_->clipboard->c.x;
550         b_item->c.y=this_->clipboard->c.y;
551         b_item->label=g_strdup(this_->clipboard->label);
552         b_item->type=this_->clipboard->type;
553         b_item->item=this_->clipboard->item;
554         b_item->parent=this_->current;
555         b_item->children=this_->clipboard->children;
556
557         g_hash_table_insert(this_->bookmarks_hash,b_item->label,b_item);
558         this_->bookmarks_list=g_list_append(this_->bookmarks_list,b_item);
559         this_->current->children=g_list_append(this_->current->children,b_item);
560         this_->current->children=g_list_first(this_->current->children);
561
562         result=bookmarks_store_bookmarks_to_file(this_,0,0);
563
564         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
565         bookmarks_clear_hash(this_);
566         bookmarks_load_hash(this_);
567
568         return result;
569 }
570
571
572 int
573 bookmarks_delete_bookmark(struct bookmarks *this_, const char *label) {
574         int result;
575
576         bookmarks_item_rewind(this_);
577         if (this_->current->children==NULL) {
578                 return 0;
579         }
580         while (this_->current->iter!=NULL) {
581                 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
582                 if (!strcmp(data->label,label)) {
583                         this_->bookmarks_list=g_list_first(this_->bookmarks_list);
584                         this_->bookmarks_list=g_list_remove(this_->bookmarks_list,data);
585
586                         result=bookmarks_store_bookmarks_to_file(this_,0,0);
587
588                         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
589                         bookmarks_clear_hash(this_);
590                         bookmarks_load_hash(this_);
591
592                         bookmarks_emit_dbus_signal(this_,&(data->c),label,FALSE);
593
594                         return result;
595                 }
596                 this_->current->iter=g_list_next(this_->current->iter);
597         }
598
599         return FALSE;
600 }
601
602 int
603 bookmarks_rename_bookmark(struct bookmarks *this_, const char *oldName, const char* newName) {
604         int result;
605
606         bookmarks_item_rewind(this_);
607         if (this_->current->children==NULL) {
608                 return 0;
609         }
610         while (this_->current->iter!=NULL) {
611                 struct bookmark_item_priv* data=(struct bookmark_item_priv*)this_->current->iter->data;
612                 if (!strcmp(data->label,oldName)) {
613                         g_free(data->label);
614                         data->label=g_strdup(newName);
615
616                         result=bookmarks_store_bookmarks_to_file(this_,0,0);
617
618                         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
619                         bookmarks_clear_hash(this_);
620                         bookmarks_load_hash(this_);
621
622                         return result;
623                 }
624                 this_->current->iter=g_list_next(this_->current->iter);
625         }
626
627         return FALSE;
628 }
629
630 struct former_destination{
631         enum item_type type;
632         char* description;
633         struct coord c;
634 };
635
636 static void free_former_destination(struct former_destination* former_destination){
637         g_free(former_destination->description);
638         g_free(former_destination);
639 }
640
641
642 static GList* read_former_destination_map_as_list(struct map *map){
643         struct map_rect *mr;
644         struct item *item;
645         struct attr attr;
646         struct former_destination *dest;
647         GList* list = NULL;
648         if (map && (mr=map_rect_new(map, NULL))) {
649                 while ((item=map_rect_get_item(mr))) {
650                         if (item->type != type_former_destination) continue;
651                         dest = g_new(struct former_destination, 1);
652                         dest->type=item->type;
653                         item_attr_get(item, attr_label, &attr);
654                         dest->description = g_strdup(attr.u.str);
655                         item_coord_get(item, &(dest->c), 1);
656                         list = g_list_prepend(list, dest);
657                 }
658                 map_rect_destroy(mr);
659         }
660         return g_list_reverse(list);
661 }
662
663 static int
664 destination_equal(struct former_destination* dest1, struct former_destination* dest2)
665 {
666         if ((dest1->type == dest2->type) &&
667             (!strcmp(dest1->description, dest2->description)) &&
668             (coord_equal(&(dest1->c), &(dest2->c)))){
669                 return TRUE;
670         }
671         return FALSE;
672 }
673
674 static GList*
675 remove_destination_from_list(struct former_destination* dest_to_remove, GList* former_destinations)
676 {
677         GList* curr_el = former_destinations; 
678         GList* prev_el = NULL;
679         struct former_destination* curr_dest;
680
681         while(curr_el){
682                 curr_dest = curr_el->data;
683                 if (destination_equal(dest_to_remove, curr_dest)){
684                         free_former_destination(curr_dest);
685                         curr_el = g_list_remove(curr_el, curr_dest);
686                 }else{
687                         prev_el = curr_el;
688                         curr_el = g_list_next(curr_el);
689                 }
690         }
691         return g_list_first(prev_el);
692 }
693
694 static void 
695 write_former_destinations(GList* former_destinations, char *former_destination_file, enum projection proj)
696 {
697         FILE *f;
698         GList* currdest = NULL;
699         struct former_destination *dest;
700         const char* prostr = projection_to_name(proj);
701         f=fopen(former_destination_file, "w");
702         if (f) {
703                 for(currdest = former_destinations; currdest; currdest = g_list_next(currdest)){
704                         dest = currdest->data;
705                         if (dest->description) 
706                                 fprintf(f,"type=%s label=\"%s\"\n", item_to_name(dest->type), dest->description);
707                         else
708                                 fprintf(f,"type=%s\n", item_to_name(dest->type));
709                         fprintf(f,"%s%s%s0x%x %s0x%x\n",
710                         prostr, *prostr ? ":" : "",
711                         dest->c.x >= 0 ? "":"-", dest->c.x >= 0 ? dest->c.x : -dest->c.x,
712                         dest->c.y >= 0 ? "":"-", dest->c.y >= 0 ? dest->c.y : -dest->c.y);
713                         
714                 }
715                 fclose(f);
716         } else {
717                 dbg(0, "Error updating destinations file %s: %s\n", former_destination_file, strerror(errno));
718         }
719 }
720
721 /**
722  * @param limit Limits the number of entries in the "backlog". Set to 0 for "infinite"
723  */
724 void
725 bookmarks_append_coord(struct map *former_destination_map, char *former_destination_file, 
726                 struct pcoord *c, enum item_type type, const char *description, int limit)
727 {
728         struct former_destination *new_dest;
729         GList* former_destinations = NULL;
730         GList* former_destinations_shortened = NULL;
731         int no_of_former_destinations;
732
733         former_destinations = read_former_destination_map_as_list(former_destination_map);
734
735         new_dest = g_new(struct former_destination, 1);
736         new_dest->type = type;
737         new_dest->description = g_strdup(description?description:_("Map Point"));
738         new_dest->c.x = c->x;
739         new_dest->c.y = c->y;
740         former_destinations = remove_destination_from_list(new_dest, former_destinations);
741         former_destinations = g_list_append(former_destinations, new_dest);
742
743         no_of_former_destinations = g_list_length(former_destinations);
744         if (limit > 0 && no_of_former_destinations > limit)
745                 former_destinations_shortened = g_list_nth(former_destinations, no_of_former_destinations - limit);
746         else
747                 former_destinations_shortened = former_destinations;
748
749         write_former_destinations(former_destinations_shortened, former_destination_file, map_projection(former_destination_map));
750         g_list_foreach(former_destinations, (GFunc) free_former_destination, NULL);
751         g_list_free(former_destinations);
752 }
753