Fix:map_csv:Disable default notification of each deleted item.
[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 int bookmarks_get_bookmark_count(struct bookmarks* this_) {
134         int ret=0;
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) {
139                         ret++;
140                 }
141                 this_->current->iter=g_list_next(this_->current->iter);
142         }
143         return ret;
144 }
145
146 const char* bookmarks_item_cwd(struct bookmarks* this_) {
147         return this_->current->label;
148 }
149
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);
155         }
156         g_free(b_item->label);
157         g_free(b_item);
158 }
159
160 static void
161 bookmarks_clear_hash(struct bookmarks *this_) {
162         if (this_->mr) {
163             map_rect_destroy(this_->mr);
164         }
165         bookmarks_clear_item(this_->root);
166         g_hash_table_destroy(this_->bookmarks_hash);
167         g_list_free(this_->bookmarks_list);
168 }
169
170 static void
171 bookmarks_load_hash(struct bookmarks *this_) {
172         struct bookmark_item_priv *b_item;
173         struct item *item;
174         struct attr attr;
175         struct coord c;
176         char *pos,*finder;
177         char *copy_helper;
178
179         if (this_->mr) {
180                 map_rect_destroy(this_->mr);
181         }
182         this_->mr=map_rect_new(this_->bookmark, NULL);
183
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_);
190
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);
195                 }
196                 item_coord_get(item, &c, 1);
197
198                 b_item=g_new0(struct bookmark_item_priv,1);
199                 b_item->c.x=c.x;
200                 b_item->c.y=c.y;
201                 b_item->label=g_strdup(attr.u.str);
202                 b_item->type=item->type;
203                 b_item->item=*item;
204
205                 //Prepare position
206                 bookmarks_move_root(this_);
207                 finder=b_item->label;
208                 while ((pos=strchr(finder,'/'))) {
209                         *pos=0x00;
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);
217
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);
222                         }
223                         finder+=strlen(finder)+1;
224                 }
225                 copy_helper=g_strdup(finder);
226                 free(b_item->label);
227                 b_item->label=copy_helper;
228                 b_item->parent=this_->current;
229
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));
235         }
236         bookmarks_move_root(this_);
237 }
238
239 struct bookmarks *
240 bookmarks_new(struct attr *parent, struct attr **attrs, struct transformation *trans) {
241         struct bookmarks *this_;
242
243         if (parent->type!=attr_navit) {
244                 return NULL;
245         }
246
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);
251         this_->trans=trans;
252
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);
255
256         this_->clipboard=g_new0(struct bookmark_item_priv,1);
257
258         {
259                 //Load map now
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)
264                         return NULL;
265                 bookmarks_load_hash(this_);
266         }
267
268         return this_;
269 }
270
271 void
272 bookmarks_destroy(struct bookmarks *this_) {
273
274         bookmarks_clear_hash(this_);
275
276         map_destroy(this_->bookmark);
277         callback_list_destroy(this_->attr_cbl);
278
279         g_free(this_->bookmark_file);
280         g_free(this_->working_file);
281
282         g_free(this_->clipboard);
283         g_free(this_);
284 }
285
286 struct map*
287 bookmarks_get_map(struct bookmarks *this_) {
288         return this_->bookmark;
289 }
290
291 enum projection bookmarks_get_projection(struct bookmarks *this_){
292         return map_projection(this_->bookmark);
293 }
294 void
295 bookmarks_add_callback(struct bookmarks *this_, struct callback *cb)
296 {
297         callback_list_add(this_->attr_cbl, cb);
298 }
299
300 static int
301 bookmarks_store_bookmarks_to_file(struct bookmarks *this_,  int limit,int replace) {
302         FILE *f;
303         struct bookmark_item_priv *item,*parent_item;
304         char *fullname;
305         const char *prostr;
306         int result;
307         GHashTable *dedup=g_hash_table_new_full(g_str_hash,g_str_equal,g_free,NULL);
308
309         f=fopen(this_->working_file, replace ? "w+" : "a+");
310         if (f==NULL) {
311                 navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file"));
312                 return FALSE;
313         }
314
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;
318
319                 parent_item=item;
320                 fullname=g_strdup(item->label);
321                 while ((parent_item=parent_item->parent)) {
322                         char *pathHelper;
323                         if (parent_item->label) {
324                                 pathHelper=g_strconcat(parent_item->label,"/",fullname,NULL);
325                                 g_free(fullname);
326                                 fullname=g_strdup(pathHelper);
327                                 g_free(pathHelper);
328                                 dbg(1,"full name: %s\n",fullname);
329                         }
330                 }
331
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) {
341                                         g_free(fullname);
342                                         break;
343                                 }
344                         }
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 ? ":" : "",
349                                          "", 0,
350                                          "", 0,
351                                          "bookmark_folder", item->label,fullname)<1) {
352                                         g_free(fullname);
353                                         break;
354                                 }
355                         }
356                 }
357
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 */
361                 if (limit==1) {
362                         break;
363                 }
364                 limit--;
365
366                 this_->bookmarks_list=g_list_next(this_->bookmarks_list);
367         }
368
369         fclose(f);
370
371     g_hash_table_destroy(dedup);
372
373     if (this_->mr) {
374         map_rect_destroy(this_->mr);
375         this_->mr = 0;
376     }
377
378     unlink(this_->bookmark_file);
379         result=(rename(this_->working_file,this_->bookmark_file)==0);
380         if (!result) 
381         {
382                 navit_add_message(this_->parent->u.navit,_("Failed to write bookmarks file"));
383         }
384         return result;
385 }
386
387 /**
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.
392  *
393  */
394 char*
395 bookmarks_get_destination_file(gboolean create)
396 {
397         return g_strjoin(NULL, navit_get_user_data_directory(create), "/destination.txt", NULL);
398 }
399
400 /*
401  * bookmarks_get_center_file
402  *
403  * returns the name of the file used to store the center file  with its
404  * full path
405  *
406  * arg: gboolean create: create the directory where the file is stored
407  * if it does not exist
408  */
409 char*
410 bookmarks_get_center_file(gboolean create)
411 {
412         return g_strjoin(NULL, navit_get_user_data_directory(create), "/center.txt", NULL);
413 }
414
415 void
416 bookmarks_set_center_from_file(struct bookmarks *this_, char *file)
417 {
418         FILE *f;
419         char *line = NULL;
420
421         size_t line_size = 0;
422         enum projection pro;
423         struct coord *center;
424
425         f = fopen(file, "r");
426         if (! f)
427                 return;
428         getline(&line, &line_size, f);
429         fclose(f);
430         if (line) {
431                 center = transform_center(this_->trans);
432                 pro = transform_get_projection(this_->trans);
433                 coord_parse(g_strchomp(line), pro, center);
434                 free(line);
435         }
436         return;
437 }
438
439 void
440 bookmarks_write_center_to_file(struct bookmarks *this_, char *file)
441 {
442         FILE *f;
443         enum projection pro;
444         struct coord *center;
445
446         f = fopen(file, "w+");
447         if (f) {
448                 center = transform_center(this_->trans);
449                 pro = transform_get_projection(this_->trans);
450                 coord_print(pro, center, f);
451                 fclose(f);
452         } else {
453                 perror(file);
454         }
455         return;
456 }
457
458 static void
459 bookmarks_emit_dbus_signal(struct bookmarks *this_, struct pcoord *c, const char *description,int create)
460 {
461     struct attr attr1,attr2,attr3,attr4,cb,*attr_list[5];
462         int valid=0;
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;
470     attr4.u.pcoord=c;
471         attr_list[0]=&attr1;
472         attr_list[1]=&attr2;
473     attr_list[2]=&attr3;
474     attr_list[3]=&attr4;
475         attr_list[4]=NULL;
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);
478 }
479
480 /**
481  * Record the given set of coordinates as a bookmark
482  *
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
486  * @returns nothing
487  */
488 int
489 bookmarks_add_bookmark(struct bookmarks *this_, struct pcoord *pc, const char *description)
490 {
491         struct bookmark_item_priv *b_item=g_new0(struct bookmark_item_priv,1);
492         int result;
493
494         if (pc) {
495                 b_item->c.x=pc->x;
496                 b_item->c.y=pc->y;
497                 b_item->type=type_bookmark;
498         } else {
499                 b_item->type=type_bookmark_folder;
500         }
501         b_item->label=g_strdup(description);
502         b_item->parent=this_->current;
503         b_item->children=NULL;
504
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);
509
510         result=bookmarks_store_bookmarks_to_file(this_,0,0);
511
512         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
513         bookmarks_clear_hash(this_);
514         bookmarks_load_hash(this_);
515
516     bookmarks_emit_dbus_signal(this_,&(b_item->c),description,TRUE);
517
518         return result;
519 }
520
521 int
522 bookmarks_cut_bookmark(struct bookmarks *this_, const char *label) {
523         if (bookmarks_copy_bookmark(this_,label)) {
524                 return bookmarks_delete_bookmark(this_,label);
525         }
526
527         return FALSE;
528 }
529 int
530 bookmarks_copy_bookmark(struct bookmarks *this_, const char *label) {
531         bookmarks_item_rewind(this_);
532         if (this_->current->children==NULL) {
533                 return 0;
534         }
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);
544                         }
545                         this_->clipboard->label=g_strdup(data->label);
546                         return TRUE;
547                 }
548                 this_->current->iter=g_list_next(this_->current->iter);
549         }
550         return FALSE;
551 }
552 int
553 bookmarks_paste_bookmark(struct bookmarks *this_) {
554         int result;
555         struct bookmark_item_priv* b_item;
556
557         if (!this_->clipboard->label) {
558             return FALSE;
559     }
560
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;
569
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);
574
575         result=bookmarks_store_bookmarks_to_file(this_,0,0);
576
577         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
578         bookmarks_clear_hash(this_);
579         bookmarks_load_hash(this_);
580
581         return result;
582 }
583
584
585 int
586 bookmarks_delete_bookmark(struct bookmarks *this_, const char *label) {
587         int result;
588
589         bookmarks_item_rewind(this_);
590         if (this_->current->children==NULL) {
591                 return 0;
592         }
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);
598
599                         result=bookmarks_store_bookmarks_to_file(this_,0,0);
600
601                         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
602                         bookmarks_clear_hash(this_);
603                         bookmarks_load_hash(this_);
604
605                         bookmarks_emit_dbus_signal(this_,&(data->c),label,FALSE);
606
607                         return result;
608                 }
609                 this_->current->iter=g_list_next(this_->current->iter);
610         }
611
612         return FALSE;
613 }
614
615 int
616 bookmarks_rename_bookmark(struct bookmarks *this_, const char *oldName, const char* newName) {
617         int result;
618
619         bookmarks_item_rewind(this_);
620         if (this_->current->children==NULL) {
621                 return 0;
622         }
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)) {
626                         g_free(data->label);
627                         data->label=g_strdup(newName);
628
629                         result=bookmarks_store_bookmarks_to_file(this_,0,0);
630
631                         callback_list_call_attr_0(this_->attr_cbl, attr_bookmark_map);
632                         bookmarks_clear_hash(this_);
633                         bookmarks_load_hash(this_);
634
635                         return result;
636                 }
637                 this_->current->iter=g_list_next(this_->current->iter);
638         }
639
640         return FALSE;
641 }
642
643 struct former_destination{
644         enum item_type type;
645         char* description;
646         struct coord c;
647 };
648
649 static void free_former_destination(struct former_destination* former_destination){
650         g_free(former_destination->description);
651         g_free(former_destination);
652 }
653
654
655 static GList* read_former_destination_map_as_list(struct map *map){
656         struct map_rect *mr;
657         struct item *item;
658         struct attr attr;
659         struct former_destination *dest;
660         GList* list = NULL;
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);
670                 }
671                 map_rect_destroy(mr);
672         }
673         return g_list_reverse(list);
674 }
675
676 static int
677 destination_equal(struct former_destination* dest1, struct former_destination* dest2)
678 {
679         if ((dest1->type == dest2->type) &&
680             (!strcmp(dest1->description, dest2->description)) &&
681             (coord_equal(&(dest1->c), &(dest2->c)))){
682                 return TRUE;
683         }
684         return FALSE;
685 }
686
687 static GList*
688 remove_destination_from_list(struct former_destination* dest_to_remove, GList* former_destinations)
689 {
690         GList* curr_el = former_destinations; 
691         GList* prev_el = NULL;
692         struct former_destination* curr_dest;
693
694         while(curr_el){
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);
699                 }else{
700                         prev_el = curr_el;
701                         curr_el = g_list_next(curr_el);
702                 }
703         }
704         return g_list_first(prev_el);
705 }
706
707 static void 
708 write_former_destinations(GList* former_destinations, char *former_destination_file, enum projection proj)
709 {
710         FILE *f;
711         GList* currdest = NULL;
712         struct former_destination *dest;
713         const char* prostr = projection_to_name(proj);
714         f=fopen(former_destination_file, "w");
715         if (f) {
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);
720                         else
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);
726                         
727                 }
728                 fclose(f);
729         } else {
730                 dbg(0, "Error updating destinations file %s: %s\n", former_destination_file, strerror(errno));
731         }
732 }
733
734 /**
735  * @param limit Limits the number of entries in the "backlog". Set to 0 for "infinite"
736  */
737 void
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)
740 {
741         struct former_destination *new_dest;
742         GList* former_destinations = NULL;
743         GList* former_destinations_shortened = NULL;
744         int no_of_former_destinations;
745
746         former_destinations = read_former_destination_map_as_list(former_destination_map);
747
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);
755
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);
759         else
760                 former_destinations_shortened = former_destinations;
761
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);
765 }
766