d74280d3189f7f479a462bc2600e91b9ea4595b8
[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  * bookmarks_get_destination_file
376  *
377  * returns the name of the file used to store destinations with its
378  * full path
379  *
380  * arg: gboolean create: create the directory where the file is stored
381  * if it does not exist
382  */
383 char*
384 bookmarks_get_destination_file(gboolean create)
385 {
386         return g_strjoin(NULL, navit_get_user_data_directory(create), "/destination.txt", NULL);
387 }
388
389 /*
390  * bookmarks_get_center_file
391  *
392  * returns the name of the file used to store the center file  with its
393  * full path
394  *
395  * arg: gboolean create: create the directory where the file is stored
396  * if it does not exist
397  */
398 char*
399 bookmarks_get_center_file(gboolean create)
400 {
401         return g_strjoin(NULL, navit_get_user_data_directory(create), "/center.txt", NULL);
402 }
403
404 void
405 bookmarks_set_center_from_file(struct bookmarks *this_, char *file)
406 {
407         FILE *f;
408         char *line = NULL;
409
410         size_t line_size = 0;
411         enum projection pro;
412         struct coord *center;
413
414         f = fopen(file, "r");
415         if (! f)
416                 return;
417         getline(&line, &line_size, f);
418         fclose(f);
419         if (line) {
420                 center = transform_center(this_->trans);
421                 pro = transform_get_projection(this_->trans);
422                 coord_parse(g_strchomp(line), pro, center);
423                 free(line);
424         }
425         return;
426 }
427
428 void
429 bookmarks_write_center_to_file(struct bookmarks *this_, char *file)
430 {
431         FILE *f;
432         enum projection pro;
433         struct coord *center;
434
435         f = fopen(file, "w+");
436         if (f) {
437                 center = transform_center(this_->trans);
438                 pro = transform_get_projection(this_->trans);
439                 coord_print(pro, center, f);
440                 fclose(f);
441         } else {
442                 perror(file);
443         }
444         return;
445 }
446
447 static void
448 bookmarks_emit_dbus_signal(struct bookmarks *this_, struct pcoord *c, const char *description,int create)
449 {
450     struct attr attr1,attr2,attr3,attr4,cb,*attr_list[5];
451         int valid=0;
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;
459     attr4.u.pcoord=c;
460         attr_list[0]=&attr1;
461         attr_list[1]=&attr2;
462     attr_list[2]=&attr3;
463     attr_list[3]=&attr4;
464         attr_list[4]=NULL;
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);
467 }
468
469 /**
470  * Record the given set of coordinates as a bookmark
471  *
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
475  * @returns nothing
476  */
477 int
478 bookmarks_add_bookmark(struct bookmarks *this_, struct pcoord *pc, const char *description)
479 {
480         struct bookmark_item_priv *b_item=g_new0(struct bookmark_item_priv,1);
481         int result;
482
483         if (pc) {
484                 b_item->c.x=pc->x;
485                 b_item->c.y=pc->y;
486                 b_item->type=type_bookmark;
487         } else {
488                 b_item->type=type_bookmark_folder;
489         }
490         b_item->label=g_strdup(description);
491         b_item->parent=this_->current;
492         b_item->children=NULL;
493
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);
498
499         result=bookmarks_store_bookmarks_to_file(this_,0,0);
500
501         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
502         bookmarks_clear_hash(this_);
503         bookmarks_load_hash(this_);
504
505     bookmarks_emit_dbus_signal(this_,&(b_item->c),description,TRUE);
506
507         return result;
508 }
509
510 int
511 bookmarks_cut_bookmark(struct bookmarks *this_, const char *label) {
512         if (bookmarks_copy_bookmark(this_,label)) {
513                 return bookmarks_delete_bookmark(this_,label);
514         }
515
516         return FALSE;
517 }
518 int
519 bookmarks_copy_bookmark(struct bookmarks *this_, const char *label) {
520         bookmarks_item_rewind(this_);
521         if (this_->current->children==NULL) {
522                 return 0;
523         }
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);
533                         }
534                         this_->clipboard->label=g_strdup(data->label);
535                         return TRUE;
536                 }
537                 this_->current->iter=g_list_next(this_->current->iter);
538         }
539         return FALSE;
540 }
541 int
542 bookmarks_paste_bookmark(struct bookmarks *this_) {
543         int result;
544         struct bookmark_item_priv* b_item;
545
546         if (!this_->clipboard->label) {
547             return FALSE;
548     }
549
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;
558
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);
563
564         result=bookmarks_store_bookmarks_to_file(this_,0,0);
565
566         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
567         bookmarks_clear_hash(this_);
568         bookmarks_load_hash(this_);
569
570         return result;
571 }
572
573
574 int
575 bookmarks_delete_bookmark(struct bookmarks *this_, const char *label) {
576         int result;
577
578         bookmarks_item_rewind(this_);
579         if (this_->current->children==NULL) {
580                 return 0;
581         }
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);
587
588                         result=bookmarks_store_bookmarks_to_file(this_,0,0);
589
590                         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
591                         bookmarks_clear_hash(this_);
592                         bookmarks_load_hash(this_);
593
594                         bookmarks_emit_dbus_signal(this_,&(data->c),label,FALSE);
595
596                         return result;
597                 }
598                 this_->current->iter=g_list_next(this_->current->iter);
599         }
600
601         return FALSE;
602 }
603
604 int
605 bookmarks_rename_bookmark(struct bookmarks *this_, const char *oldName, const char* newName) {
606         int result;
607
608         bookmarks_item_rewind(this_);
609         if (this_->current->children==NULL) {
610                 return 0;
611         }
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)) {
615                         g_free(data->label);
616                         data->label=g_strdup(newName);
617
618                         result=bookmarks_store_bookmarks_to_file(this_,0,0);
619
620                         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
621                         bookmarks_clear_hash(this_);
622                         bookmarks_load_hash(this_);
623
624                         return result;
625                 }
626                 this_->current->iter=g_list_next(this_->current->iter);
627         }
628
629         return FALSE;
630 }
631
632 struct former_destination{
633         enum item_type type;
634         char* description;
635         struct coord c;
636 };
637
638 static void free_former_destination(struct former_destination* former_destination){
639         g_free(former_destination->description);
640         g_free(former_destination);
641 }
642
643
644 static GList* read_former_destination_map_as_list(struct map *map){
645         struct map_rect *mr;
646         struct item *item;
647         struct attr attr;
648         struct former_destination *dest;
649         GList* list = NULL;
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);
659                 }
660                 map_rect_destroy(mr);
661         }
662         return g_list_reverse(list);
663 }
664
665 static int
666 destination_equal(struct former_destination* dest1, struct former_destination* dest2)
667 {
668         if ((dest1->type == dest2->type) &&
669             (!strcmp(dest1->description, dest2->description)) &&
670             (coord_equal(&(dest1->c), &(dest2->c)))){
671                 return TRUE;
672         }
673         return FALSE;
674 }
675
676 static GList*
677 remove_destination_from_list(struct former_destination* dest_to_remove, GList* former_destinations)
678 {
679         GList* curr_el = former_destinations; 
680         GList* prev_el = NULL;
681         struct former_destination* curr_dest;
682
683         while(curr_el){
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);
688                 }else{
689                         prev_el = curr_el;
690                         curr_el = g_list_next(curr_el);
691                 }
692         }
693         return g_list_first(prev_el);
694 }
695
696 static void 
697 write_former_destinations(GList* former_destinations, char *former_destination_file, enum projection proj)
698 {
699         FILE *f;
700         GList* currdest = NULL;
701         struct former_destination *dest;
702         const char* prostr = projection_to_name(proj);
703         f=fopen(former_destination_file, "w");
704         if (f) {
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);
709                         else
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);
715                         
716                 }
717                 fclose(f);
718         } else {
719                 dbg(0, "Error updating destinations file %s: %s\n", former_destination_file, strerror(errno));
720         }
721 }
722
723 /**
724  * @param limit Limits the number of entries in the "backlog". Set to 0 for "infinite"
725  */
726 void
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)
729 {
730         struct former_destination *new_dest;
731         GList* former_destinations = NULL;
732         GList* former_destinations_shortened = NULL;
733         int no_of_former_destinations;
734
735         former_destinations = read_former_destination_map_as_list(former_destination_map);
736
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);
744
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);
748         else
749                 former_destinations_shortened = former_destinations;
750
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);
754 }
755