Contents of /trunk/src/notes.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 133 - (hide annotations)
Mon Oct 12 20:27:55 2009 UTC (14 years, 7 months ago) by harbaum
File MIME type: text/plain
File size: 30723 byte(s)
Various fremantle fixes
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of GPXView.
5     *
6     * GPXView is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * GPXView is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include <stdio.h>
21     #include <string.h>
22    
23     #include <glib/gunicode.h>
24    
25     #include <libxml/parser.h>
26     #include <libxml/tree.h>
27    
28     #include <math.h>
29    
30     #if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED)
31     #error "libxml doesn't support required tree or output"
32     #endif
33    
34     #include "gpxview.h"
35    
36     void gtk_text_buffer_set_can_paste_rich_text(GtkTextBuffer *buffer, gboolean);
37     void gtk_text_buffer_set_rich_text_format(GtkTextBuffer *buffer, const gchar *);
38    
39     #define TAG_STATE GTK_STATE_PRELIGHT
40    
41     static notes_t *notes_load(appdata_t *appdata, cache_t *cache) {
42     notes_t *notes = NULL;
43     xmlDoc *doc = NULL;
44     xmlNode *root_element = NULL;
45    
46     LIBXML_TEST_VERSION;
47    
48     /* build local path */
49     int path_len = strlen(appdata->image_path) + 2 * strlen(cache->id) + 6;
50     char *path = malloc(path_len);
51     snprintf(path, path_len, "%s%s/%s.gpx",
52     appdata->image_path, cache->id, cache->id);
53    
54     /* no such file? */
55     if(!g_file_test(path, G_FILE_TEST_EXISTS)) {
56     free(path);
57     return NULL;
58     }
59    
60     /* parse the file and get the DOM */
61     doc = xmlReadFile(path, NULL, 0);
62    
63     if(doc == NULL) {
64     printf("error: could not parse file %s\n", path);
65     free(path);
66     return NULL;
67     }
68    
69     /* Get the root element node */
70     root_element = xmlDocGetRootElement(doc);
71    
72     xmlNode *cur_node = NULL;
73     for (cur_node = root_element; cur_node; cur_node = cur_node->next) {
74     if (cur_node->type == XML_ELEMENT_NODE) {
75     if(strcasecmp((char*)cur_node->name, "gpx") == 0) {
76     xmlNode *wpt_node = cur_node->children;
77    
78     while(wpt_node != NULL) {
79     if(wpt_node->type == XML_ELEMENT_NODE) {
80     if(strcasecmp((char*)wpt_node->name, "wpt") == 0) {
81    
82     notes = malloc(sizeof(notes_t));
83     memset(notes, 0, sizeof(notes_t));
84     notes->pos = gpx_cache_pos(cache);
85    
86     char *str;
87     if((str = (char*)xmlGetProp(wpt_node, BAD_CAST "override"))) {
88     notes->override = (strcasecmp(str, "yes") == 0);
89     xmlFree(str);
90     }
91    
92     if((str = (char*)xmlGetProp(wpt_node, BAD_CAST "found"))) {
93     /* check if there's a valid number -> found time */
94     if(strtoul(str, NULL, 10) != 0) {
95     notes->found = TRUE;
96     notes->ftime = strtoul(str, NULL, 10);
97     } else {
98     notes->found = (strcasecmp(str, "yes") == 0);
99     notes->ftime = 0;
100     }
101     xmlFree(str);
102     }
103    
104     if((str = (char*)xmlGetProp(wpt_node, BAD_CAST "logged"))) {
105     notes->logged = (strcasecmp(str, "yes") == 0);
106     xmlFree(str);
107     }
108    
109     if((str = (char*)xmlGetProp(wpt_node, BAD_CAST "lat"))) {
110     notes->pos.lat = g_ascii_strtod(str, NULL);
111     xmlFree(str);
112     }
113    
114     if((str = (char*)xmlGetProp(wpt_node, BAD_CAST "lon"))) {
115     notes->pos.lon = g_ascii_strtod(str, NULL);
116     xmlFree(str);
117     }
118    
119     xmlNode *sub_node = wpt_node->children;
120    
121     while (sub_node != NULL) {
122     if (sub_node->type == XML_ELEMENT_NODE) {
123     if(strcasecmp((char*)sub_node->name, "desc") == 0)
124     notes->text = (char*)
125     xmlNodeListGetString(doc, sub_node->children, 1);
126     }
127     sub_node = sub_node->next;
128     }
129     }
130     }
131     wpt_node = wpt_node->next;
132     }
133     }
134     }
135     }
136    
137     xmlFreeDoc(doc);
138     xmlCleanupParser();
139    
140     printf("got notes for cache %s\n", cache->id);
141    
142     free(path);
143     return notes;
144     }
145    
146     void notes_load_all(appdata_t *appdata, gpx_t *gpx) {
147     printf("Load all notes for %s\n", gpx->name);
148    
149     cache_t *cache = gpx->cache;
150     while(cache) {
151     /* a note may actually already be there if this gpx file */
152     /* is e.g. assembled from search results */
153     if(!cache->notes)
154     cache->notes = notes_load(appdata, cache);
155    
156     cache = cache->next;
157     }
158     }
159    
160 harbaum 133 static int notes_write_file(cache_context_t *context,
161 harbaum 1 char *text, pos_t pos,
162     gboolean override, gboolean found,
163     time_t ftime, gboolean logged) {
164     /* build local path */
165     int path_len = strlen(context->appdata->image_path) +
166     2 * strlen(context->cache->id) + 6;
167     char *path = malloc(path_len);
168     snprintf(path, path_len, "%s%s/%s.gpx",
169     context->appdata->image_path,
170     context->cache->id, context->cache->id);
171    
172     if(checkdir(path) != 0) {
173     printf("unable to create notes path\n");
174     free(path);
175     return -1;
176     }
177    
178     LIBXML_TEST_VERSION;
179    
180     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
181     xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "gpx");
182     xmlDocSetRootElement(doc, root_node);
183    
184     xmlNodePtr wpt_node = xmlNewChild(root_node, NULL, BAD_CAST "wpt", NULL);
185     /* make sure no invalid position gets saved */
186     if(!isnan(pos.lat) && !isnan(pos.lon)) {
187     if(override)
188     xmlNewProp(wpt_node, BAD_CAST "override", BAD_CAST "yes");
189     else
190     xmlNewProp(wpt_node, BAD_CAST "override", BAD_CAST "no");
191    
192     if(logged)
193     xmlNewProp(wpt_node, BAD_CAST "logged", BAD_CAST "yes");
194     else
195     xmlNewProp(wpt_node, BAD_CAST "logged", BAD_CAST "no");
196    
197     if(found) {
198     if(ftime) {
199     char str[32];
200     snprintf(str, sizeof(str), "%lu", ftime);
201     xmlNewProp(wpt_node, BAD_CAST "found", BAD_CAST str);
202     } else
203     xmlNewProp(wpt_node, BAD_CAST "found", BAD_CAST "yes");
204     } else
205     xmlNewProp(wpt_node, BAD_CAST "found", BAD_CAST "no");
206    
207     char str[32];
208     g_ascii_dtostr(str, sizeof(str), pos.lat);
209     xmlNewProp(wpt_node, BAD_CAST "lat", BAD_CAST str);
210     g_ascii_dtostr(str, sizeof(str), pos.lon);
211     xmlNewProp(wpt_node, BAD_CAST "lon", BAD_CAST str);
212     }
213    
214     int len = strlen(context->cache->id) + strlen(" - ") +
215     strlen(context->cache->name) + 1;
216     char *name = malloc(len);
217     snprintf(name, len, "%s - %s", context->cache->id, context->cache->name);
218     xmlNewChild(wpt_node, NULL, BAD_CAST "name", BAD_CAST name);
219     free(name);
220     xmlNewChild(wpt_node, NULL, BAD_CAST "sym", BAD_CAST "Pin, Blue");
221     xmlNewChild(wpt_node, NULL, BAD_CAST "desc", BAD_CAST text);
222    
223     /* write everything and free it */
224     printf("writing %s\n", path);
225     xmlSaveFormatFileEnc(path, doc, "UTF-8", 1);
226     xmlFreeDoc(doc);
227     xmlCleanupParser();
228     free(path);
229    
230     return 0;
231     }
232    
233 harbaum 133 static void notes_save(cache_context_t *context) {
234 harbaum 1 /* only save if: there is a text which has been changed or */
235     /* there is a position which differs from the original one */
236     /* or has been changed */
237    
238     if(context->notes.modified) {
239     printf("something has been modified, saving notes\n");
240    
241     GtkTextIter start;
242     GtkTextIter end;
243     gtk_text_buffer_get_start_iter(context->notes.buffer, &start);
244     gtk_text_buffer_get_end_iter(context->notes.buffer, &end);
245     char *text = gtk_text_buffer_get_text(context->notes.buffer,
246     &start, &end, FALSE);
247    
248     pos_t pos;
249     pos.lat = lat_get(context->notes.latw);
250     pos.lon = lon_get(context->notes.lonw);
251    
252     gboolean override =
253     gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context->notes.overridew));
254     gboolean found =
255     gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context->notes.foundw));
256     gboolean logged =
257     gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context->notes.loggedw));
258    
259     /* required accuracy is 1/60000 degree */
260     if(((int)(pos.lat * 60000 + .5) !=
261     (int)(context->cache->pos.lat * 60000 + .5)) ||
262     ((int)(pos.lon * 60000 + .5) !=
263     (int)(context->cache->pos.lon * 60000 + .5)))
264     printf("position is modified %f != %f / %f != %f\n",
265     pos.lat * 60000 + .5, context->cache->pos.lat * 60000 + .5,
266     pos.lon * 60000 + .5, context->cache->pos.lon * 60000 + .5);
267     if(override || found)
268     printf("flags are set\n");
269     if(strlen(text))
270     printf("text is present\n");
271    
272     /* check if the notes are empty */
273     if(((int)(pos.lat * 60000 + .5) ==
274     (int)(context->cache->pos.lat * 60000 + .5)) &&
275     ((int)(pos.lon * 60000 + .5) ==
276     (int)(context->cache->pos.lon * 60000 + .5)) &&
277     !override && !found && !logged && (strlen(text) == 0)) {
278     printf("notes are in default state, removing them if present\n");
279    
280     /* remove note */
281     int path_len = strlen(context->appdata->image_path) +
282     2 * strlen(context->cache->id) + 6;
283     char *path = malloc(path_len);
284     snprintf(path, path_len, "%s%s/%s.gpx",
285     context->appdata->image_path,
286     context->cache->id, context->cache->id);
287    
288     printf("removing note %s\n", path);
289     remove(path);
290     free(path);
291    
292     /* search for matching caches and replace note there */
293     gpx_t *gpx = context->appdata->gpx;
294     while(gpx) {
295     cache_t *cache = gpx->cache;
296     while(cache) {
297     if(strcmp(cache->id, context->cache->id)==0) {
298     if(cache->notes) {
299     notes_free(cache->notes);
300     cache->notes = NULL;
301     }
302     }
303     cache = cache->next;
304     }
305     gpx = gpx->next;
306     }
307    
308     #ifdef USE_MAEMO
309     /* update search results if present */
310     if(context->appdata->search_results) {
311     printf("Updating search results\n");
312    
313     /* add note to all matching search results */
314     cache_t *cache = context->appdata->search_results->cache;
315     while(cache) {
316     if(strcmp(cache->id, context->cache->id)==0)
317     cache->notes = NULL;
318    
319     cache = cache->next;
320     }
321     }
322     #endif
323    
324    
325     } else {
326     /* we have to do two things here: */
327     /* - update the notes.xml file on disk */
328     /* - update the notes entry in the loaded gpx tree */
329    
330     /* update file on disk */
331 harbaum 133 notes_write_file(context, text, pos, override, found,
332     context->notes.ftime, logged);
333 harbaum 1
334     /* search for matching caches and replace note there */
335     notes_t *note = NULL;
336     gpx_t *gpx = context->appdata->gpx;
337     while(gpx) {
338     cache_t *cache = gpx->cache;
339     while(cache) {
340     if(strcmp(cache->id, context->cache->id)==0) {
341     // printf("found %s in %s\n", cache->id, gpx->name);
342    
343     if(cache->notes)
344     notes_free(cache->notes);
345    
346     /* create a new note for this cache */
347     cache->notes = note = malloc(sizeof(notes_t));
348     memset(cache->notes, 0, sizeof(notes_t));
349     cache->notes->text = strdup(text);
350     cache->notes->pos = pos;
351     cache->notes->override = override;
352     cache->notes->found = found;
353     cache->notes->logged = logged;
354     cache->notes->ftime = context->notes.ftime;
355     }
356     cache = cache->next;
357     }
358     gpx = gpx->next;
359     }
360    
361     #ifdef USE_MAEMO
362     /* update search results if present */
363     if(context->appdata->search_results) {
364     printf("Updating search results\n");
365    
366     /* add note to all matching search results */
367     cache_t *cache = context->appdata->search_results->cache;
368     while(cache) {
369     if(strcmp(cache->id, context->cache->id)==0)
370     cache->notes = note;
371    
372     cache = cache->next;
373     }
374     }
375     #endif
376     }
377    
378     if(text) free(text);
379     }
380 harbaum 133 }
381 harbaum 1
382 harbaum 133 /* this is called from the destroy event of the entire notebook */
383     gint notes_destroy_event(GtkWidget *widget, gpointer data ) {
384     cache_context_t *context = (cache_context_t*)data;
385    
386     printf("about to destroy notes view\n");
387     notes_save(context);
388    
389 harbaum 1 return FALSE;
390     }
391    
392 harbaum 2 #ifndef NO_COPY_N_PASTE
393 harbaum 1 static void on_destroy_textview(GtkWidget *widget, gpointer data) {
394     appdata_t *appdata = (appdata_t*)data;
395    
396     printf("destroying notes textview\n");
397    
398     /* only do this if main windows hasn't already been destroyed */
399     if(!appdata->window) {
400     printf("destroy notes textview: main window is gone\n");
401     return;
402     }
403    
404     if(!appdata->active_buffer)
405     printf("There is no active buffer!\n");
406     else {
407     if(appdata->active_buffer ==
408     gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget))) {
409     printf("This was the active buffer\n");
410    
411     appdata->active_buffer = NULL;
412    
413     gtk_widget_set_sensitive(appdata->menu_cut, FALSE);
414     gtk_widget_set_sensitive(appdata->menu_copy, FALSE);
415     gtk_widget_set_sensitive(appdata->menu_paste, FALSE);
416     }
417     }
418     }
419 harbaum 2 #endif
420 harbaum 1
421     static void ftime_update(GtkWidget *widget, cache_context_t *context,
422     gboolean update) {
423     /* check if it has been selected */
424     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
425     printf("set active\n");
426    
427     if(update)
428     context->notes.ftime = time(NULL);
429    
430     if(context->notes.ftime) {
431     struct tm *loctime = localtime(&context->notes.ftime);
432     char str[32];
433     strftime(str, sizeof(str), "%x %X", loctime);
434    
435     gtk_label_set_text(GTK_LABEL(context->notes.datew), str);
436     } else
437     gtk_label_set_text(GTK_LABEL(context->notes.datew), "");
438    
439     } else {
440     printf("invalidating time\n");
441     context->notes.ftime = 0;
442     gtk_label_set_text(GTK_LABEL(context->notes.datew), "");
443     }
444     }
445    
446     /* buffer edited */
447     static void callback_modified(GtkWidget *widget, gpointer data ) {
448     cache_context_t *context = (cache_context_t*)data;
449     // printf("something has been edited\n");
450     context->notes.modified = TRUE;
451    
452     if(widget == context->notes.foundw) {
453     printf("was foundw\n");
454    
455     /* about to remove "found" flag -> ask for confirmation */
456     if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
457     GtkWidget *dialog = gtk_message_dialog_new(
458     GTK_WINDOW(context->appdata->window),
459     GTK_DIALOG_DESTROY_WITH_PARENT,
460     GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
461     _("Do you really want to remove the \"found\" flag? "
462     "This will void the recorded date of your find!"));
463    
464     gtk_window_set_title(GTK_WINDOW(dialog), _("Reset \"found\" flag?"));
465    
466     /* set the active flag again if the user answered "no" */
467     if(GTK_RESPONSE_NO == gtk_dialog_run(GTK_DIALOG(dialog)))
468     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
469    
470     gtk_widget_destroy(dialog);
471     }
472    
473     ftime_update(widget, context, TRUE);
474     }
475    
476     if(widget == context->notes.loggedw) {
477     printf("was loggedw\n");
478    
479     /* about to remove "found" flag -> ask for confirmation */
480     if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
481     GtkWidget *dialog = gtk_message_dialog_new(
482     GTK_WINDOW(context->appdata->window),
483     GTK_DIALOG_DESTROY_WITH_PARENT,
484     GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
485     _("Do you really want to remove the \"logged\" flag? "
486     "This may cause problems on your next Garmin Field "
487     "Notes upload!"));
488    
489     gtk_window_set_title(GTK_WINDOW(dialog), _("Reset \"logged\" flag?"));
490    
491     /* set the active flag again if the user answered "no" */
492     if(GTK_RESPONSE_NO == gtk_dialog_run(GTK_DIALOG(dialog)))
493     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
494     else {
495     gtk_widget_set_sensitive(widget, FALSE);
496     gtk_widget_set_sensitive(context->notes.foundw, TRUE);
497     }
498    
499     gtk_widget_destroy(dialog);
500     }
501     }
502     }
503    
504 harbaum 2 #ifndef NO_COPY_N_PASTE
505 harbaum 1 static gboolean focus_in(GtkWidget *widget, GdkEventFocus *event,
506     gpointer data) {
507     appdata_t *appdata = (appdata_t*)data;
508    
509     printf("note focus in!\n");
510    
511     /* these buffers are read/write, thus all items are enabled */
512     gtk_widget_set_sensitive(appdata->menu_cut, TRUE);
513     gtk_widget_set_sensitive(appdata->menu_copy, TRUE);
514     gtk_widget_set_sensitive(appdata->menu_paste, TRUE);
515    
516     if(GTK_WIDGET_TYPE(widget) == GTK_TYPE_TEXT_VIEW) {
517     appdata->active_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
518     } else
519     printf("not a text view\n");
520    
521     return FALSE;
522     }
523 harbaum 2 #endif
524 harbaum 1
525 harbaum 133 static gboolean focus_out(GtkWidget *widget, GdkEventFocus *event,
526     gpointer data) {
527     cache_context_t *context = (cache_context_t*)data;
528    
529     notes_save(context);
530     #if !defined(USE_MAEMO) && defined(ENABLE_OSM_GPS_MAP)
531     map_update(context->appdata);
532     #endif
533    
534     return FALSE;
535     }
536    
537 harbaum 1 GtkWidget *cache_notes(cache_context_t *context) {
538     cache_t *cache = context->cache;
539    
540     context->notes.modified = FALSE;
541    
542     if(context->cache->notes)
543     context->notes.ftime = context->cache->notes->ftime;
544     else
545     context->notes.ftime = 0;
546    
547     GtkWidget *vbox = gtk_vbox_new(FALSE, 2);
548    
549     /* -------------- custom coordinate ---------------- */
550    
551     GtkWidget *table = gtk_table_new(2, 4, FALSE);
552     gtk_table_set_col_spacing(GTK_TABLE(table), 0, 16);
553     gtk_table_set_col_spacing(GTK_TABLE(table), 2, 16);
554    
555     gtk_table_attach_defaults(GTK_TABLE(table),
556     gtk_label_new(_("New coordinate:")), 0, 1, 0, 1);
557     context->notes.overridew = gtk_check_button_new_with_label(_("Override"));
558     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context->notes.overridew),
559     cache->notes && cache->notes->override);
560     gtk_table_attach_defaults(GTK_TABLE(table),
561     context->notes.overridew, 0, 1, 1, 2);
562    
563     GtkWidget *hbox = gtk_hbox_new(FALSE, 2);
564    
565     context->notes.foundw = gtk_check_button_new_with_label(_("Found"));
566     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context->notes.foundw),
567     cache->notes && cache->notes->found);
568     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->notes.foundw);
569    
570     context->notes.loggedw = gtk_check_button_new_with_label(_("Logged"));
571     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context->notes.loggedw),
572     cache->notes && cache->notes->logged);
573     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->notes.loggedw);
574    
575     gtk_table_attach_defaults(GTK_TABLE(table), hbox, 3, 4, 0, 1);
576    
577     /* only found and logged caches have a log flag the user can change */
578     if(!(cache->notes && cache->notes->found && cache->notes->logged))
579     gtk_widget_set_sensitive(context->notes.loggedw, FALSE);
580     else
581     gtk_widget_set_sensitive(context->notes.foundw, FALSE);
582    
583     context->notes.datew = gtk_label_new("");
584     gtk_misc_set_alignment(GTK_MISC(context->notes.datew), 0.0f, 0.5f);
585     gtk_table_attach_defaults(GTK_TABLE(table),
586     context->notes.datew, 3, 4, 1, 2);
587     ftime_update(context->notes.foundw, context, FALSE);
588    
589     pos_t pos = gpx_cache_pos(cache);
590     if(cache->notes) pos = cache->notes->pos;
591    
592     gtk_table_attach_defaults(GTK_TABLE(table),
593 harbaum 133 context->notes.latw = lat_entry_new(pos.lat), 2, 3, 0, 1);
594     g_signal_connect(G_OBJECT(context->notes.latw), "focus-out-event",
595     G_CALLBACK(focus_out), context);
596    
597 harbaum 1 gtk_table_attach_defaults(GTK_TABLE(table),
598 harbaum 133 context->notes.lonw = lon_entry_new(pos.lon), 2, 3, 1, 2);
599     g_signal_connect(G_OBJECT(context->notes.lonw), "focus-out-event",
600     G_CALLBACK(focus_out), context);
601 harbaum 1
602     hbox = gtk_hbox_new(FALSE, 0);
603     gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0);
604     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
605    
606     #ifndef USE_PANNABLE_AREA
607     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
608     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
609     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
610     #else
611     GtkWidget *pannable_area = hildon_pannable_area_new();
612     #endif
613    
614     context->notes.buffer = gtk_text_buffer_new(NULL);
615    
616     if(cache->notes && cache->notes->text)
617     gtk_text_buffer_set_text(context->notes.buffer, cache->notes->text, -1);
618    
619 harbaum 8 #ifndef USE_HILDON_TEXT_VIEW
620 harbaum 1 GtkWidget *view = gtk_text_view_new_with_buffer(context->notes.buffer);
621     #else
622     GtkWidget *view = hildon_text_view_new();
623     hildon_text_view_set_buffer(HILDON_TEXT_VIEW(view), context->notes.buffer);
624     #endif
625    
626     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
627     gtk_text_view_set_editable(GTK_TEXT_VIEW(view), TRUE);
628     gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 2 );
629     gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 2 );
630    
631     #ifdef USE_MAEMO
632     /* Enable Rich Text Support */
633     gtk_text_buffer_set_can_paste_rich_text(context->notes.buffer, TRUE );
634     gtk_text_buffer_set_rich_text_format(context->notes.buffer, "RTF" );
635     #endif
636    
637 harbaum 2 #ifndef NO_COPY_N_PASTE
638 harbaum 1 g_signal_connect(G_OBJECT(view), "focus-in-event",
639 harbaum 11 G_CALLBACK(focus_in), context->appdata);
640 harbaum 1 g_signal_connect(G_OBJECT(view), "destroy",
641 harbaum 11 G_CALLBACK(on_destroy_textview), context->appdata);
642 harbaum 2 #endif
643 harbaum 1
644     #ifndef USE_PANNABLE_AREA
645     gtk_container_add(GTK_CONTAINER(scrolled_window), view);
646    
647     gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(scrolled_window),
648     GTK_SHADOW_IN);
649     gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
650     #else
651     gtk_container_add(GTK_CONTAINER(pannable_area), view);
652     gtk_box_pack_start(GTK_BOX(vbox), pannable_area, TRUE, TRUE, 0);
653     #endif
654    
655     gtk_text_view_place_cursor_onscreen(GTK_TEXT_VIEW(view));
656    
657     g_signal_connect(G_OBJECT(context->notes.buffer), "modified-changed",
658     G_CALLBACK(callback_modified), context);
659     g_signal_connect(G_OBJECT(context->notes.buffer), "changed",
660     G_CALLBACK(callback_modified), context);
661     g_signal_connect(G_OBJECT(context->notes.lonw), "changed",
662     G_CALLBACK(callback_modified), context);
663     g_signal_connect(G_OBJECT(context->notes.latw), "changed",
664     G_CALLBACK(callback_modified), context);
665     g_signal_connect(G_OBJECT(context->notes.overridew), "toggled",
666     G_CALLBACK(callback_modified), context);
667     g_signal_connect(G_OBJECT(context->notes.foundw), "toggled",
668     G_CALLBACK(callback_modified), context);
669     g_signal_connect(G_OBJECT(context->notes.loggedw), "toggled",
670     G_CALLBACK(callback_modified), context);
671    
672     return vbox;
673     }
674    
675     void notes_free(notes_t *notes) {
676     if(notes) {
677     if(notes->text) xmlFree(notes->text);
678     free(notes);
679     }
680     }
681    
682     pos_t notes_get_pos(cache_context_t *context) {
683     pos_t pos = context->cache->pos;
684     if(gtk_toggle_button_get_active(
685     GTK_TOGGLE_BUTTON(context->notes.overridew))) {
686     pos.lat = lat_get(context->notes.latw);
687     pos.lon = lon_get(context->notes.lonw);
688     }
689     return pos;
690     }
691    
692     gboolean notes_get_override(cache_context_t *context) {
693     /* get override value */
694     return gtk_toggle_button_get_active(
695     GTK_TOGGLE_BUTTON(context->notes.overridew));
696    
697     }
698    
699     typedef struct {
700     GtkWidget *info_label;
701     GtkWidget *dialog;
702     appdata_t *appdata;
703     GtkWidget *path_label;
704     } export_context_t;
705    
706     static void on_browse(GtkWidget *widget, gpointer data) {
707     GtkWidget *dialog;
708    
709     export_context_t *context = (export_context_t*)data;
710    
711     #ifdef USE_MAEMO
712     dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(context->dialog),
713     GTK_FILE_CHOOSER_ACTION_SAVE);
714     #else
715     dialog = gtk_file_chooser_dialog_new(_("Save POI database"),
716     GTK_WINDOW(context->dialog),
717     GTK_FILE_CHOOSER_ACTION_SAVE,
718     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
719     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
720     NULL);
721     #endif
722    
723     printf("set filename <%s>\n", context->appdata->fieldnotes_path);
724    
725     if(!g_file_test(context->appdata->fieldnotes_path, G_FILE_TEST_EXISTS)) {
726     char *last_sep = strrchr(context->appdata->fieldnotes_path, '/');
727     if(last_sep) {
728     *last_sep = 0; // seperate path from file
729    
730     /* the user just created a new document */
731     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
732     context->appdata->fieldnotes_path);
733     gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), last_sep+1);
734    
735     /* restore full filename */
736     *last_sep = '/';
737     }
738     } else
739     gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
740     context->appdata->fieldnotes_path);
741    
742     if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_FM_OK) {
743     gchar *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
744     if(name) {
745     free(context->appdata->fieldnotes_path);
746     context->appdata->fieldnotes_path = strdup(name);
747     gtk_label_set_text(GTK_LABEL(context->path_label),
748     context->appdata->fieldnotes_path);
749     }
750     }
751    
752     gtk_widget_destroy (dialog);
753     }
754    
755     typedef struct log_chain_s {
756     cache_t *cache;
757     struct log_chain_s *next;
758     } log_chain_t;
759    
760     void notes_log_export(appdata_t *appdata) {
761     gpx_t *gpx = appdata->gpx;
762     log_chain_t *log = NULL, *llog, **clog = &log;
763    
764     export_context_t context;
765     memset(&context, 0, sizeof(export_context_t));
766     context.appdata = appdata;
767    
768     printf("export log\n");
769    
770     int logs2export = 0;
771    
772     /* make sure all notes are loaded */
773     while(gpx) {
774     if(!gpx->notes_loaded) {
775     notes_load_all(appdata, gpx);
776     gpx->notes_loaded = TRUE;
777     }
778    
779     cache_t *cache = gpx->cache;
780     while(cache) {
781     if(cache->notes && cache->notes->found && !cache->notes->logged) {
782     gboolean already_chained = FALSE;
783     llog = log;
784     while(llog) {
785     if(strcasecmp(llog->cache->id, cache->id) == 0)
786     already_chained = TRUE;
787    
788     llog = llog->next;
789     }
790    
791     if(!already_chained) {
792     logs2export++;
793    
794     printf("chaining log for %s\n", cache->id);
795    
796     *clog = g_new0(log_chain_t, 1);
797     (*clog)->cache = cache;
798     clog = &((*clog)->next);
799     } else
800     printf("dup for %s\n", cache->id);
801    
802     }
803    
804     cache = cache->next;
805     }
806    
807     gpx = gpx->next;
808     }
809    
810    
811     /* ------------- confirmation dialog ---------------- */
812     char *old_fieldnotes_path = strdup(appdata->fieldnotes_path);
813    
814     context.dialog = gtk_dialog_new_with_buttons(_("Garmin Field Notes Export"),
815     GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
816     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
817     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
818     NULL);
819    
820     #if defined(USE_MAEMO) && defined(HILDON_HELP)
821     hildon_help_dialog_help_enable(GTK_DIALOG(context.dialog),
822     HELP_ID_FIELDNOTES, appdata->osso_context);
823     #endif
824    
825     GtkWidget *vbox = gtk_vbox_new(FALSE,0);
826    
827     /* ------------------ info text -------------- */
828    
829     char *msg = g_strdup_printf(_("This will export the notes of %d caches "
830     "into the given file. This file can be "
831     "uploaded to geocaching.com for logging."),
832     logs2export);
833    
834     context.info_label = gtk_label_new(msg);
835     g_free(msg);
836     gtk_label_set_line_wrap_mode(GTK_LABEL(context.info_label), PANGO_WRAP_WORD);
837     gtk_label_set_line_wrap(GTK_LABEL(context.info_label), TRUE);
838     gtk_misc_set_alignment(GTK_MISC(context.info_label), 0.f, 0.5f);
839     gtk_box_pack_start_defaults(GTK_BOX(vbox), context.info_label);
840    
841     /* ------------------ path/file ------------------ */
842     gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_hseparator_new());
843    
844     GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
845     GtkWidget *label = gtk_label_new(_("Export to:"));
846     gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE,0);
847     gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
848 harbaum 7 GtkWidget *button = gtk_button_new_with_label(_("Browse"));
849 harbaum 1 gtk_signal_connect(GTK_OBJECT(button), "clicked",
850     GTK_SIGNAL_FUNC(on_browse), (gpointer)&context);
851     gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE,0);
852     gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);
853    
854     context.path_label = gtk_label_new(appdata->fieldnotes_path);
855     gtk_misc_set_alignment(GTK_MISC(context.path_label), 0.f, 0.5f);
856     gtk_label_set_ellipsize(GTK_LABEL(context.path_label),
857     PANGO_ELLIPSIZE_MIDDLE);
858     gtk_box_pack_start_defaults(GTK_BOX(vbox), context.path_label);
859    
860     label = gtk_label_new(_("(a %s in the filename will be replaced by the "
861     "current date and time)"));
862     gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD);
863     gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
864     gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
865     gtk_box_pack_start_defaults(GTK_BOX(vbox), label);
866    
867     /* ------------------ info ------------------ */
868    
869     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context.dialog)->vbox), vbox);
870    
871     gtk_widget_show_all(context.dialog);
872    
873     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context.dialog))) {
874    
875     /* ---------------- do actual export ------------------ */
876    
877     time_t now = time(NULL);
878     struct tm *tm_now = localtime(&now);
879     char now_str[32];
880     strftime(now_str, sizeof(now_str)-1, "%F_%H:%M", tm_now);
881     char *fname = g_strdup_printf(appdata->fieldnotes_path, now_str);
882     printf("--- about to export logs to %s ---\n", fname);
883     FILE *file = fopen(fname, "w");
884     g_free(fname);
885    
886     if(file) {
887    
888     llog = log;
889     while(llog) {
890     printf("Exporting %s\n", llog->cache->id);
891    
892     /* ----------- build utc time string ----------- */
893    
894     char *tz = getenv("TZ");
895     setenv("TZ", "", 1);
896     tzset();
897     struct tm *tm = localtime(&llog->cache->notes->ftime);
898     char tstr[32];
899     strftime(tstr, sizeof(tstr)-1, "%FT%H:%MZ", tm);
900     if(tz) setenv("TZ", tz, 1);
901     else unsetenv("TZ");
902     tzset();
903    
904     /* escape \" in text */
905     char *text = NULL;
906     if(llog->cache->notes->text) {
907     text = g_strdup(llog->cache->notes->text);
908     char *p = text;
909     while(*p) {
910     /* convert " to ' */
911     if(*p == '\"') *p = '\'';
912     p++;
913     }
914     } else
915     text = g_strdup("");
916    
917     /* ----------- build complete log string ------------ */
918     char *str = g_strdup_printf("%s,%s,Found it,\"%s\"\n",
919     llog->cache->id, tstr, text);
920     g_free(text);
921    
922     /* ----------- convert to unicode ------------ */
923     glong written = 0;
924     gunichar2 *uc = g_utf8_to_utf16(str, -1, NULL, &written, NULL);
925     g_free(str);
926    
927     /* -------------- and write it --------------- */
928     fwrite(uc, written, sizeof(gunichar2), file);
929     g_free(uc);
930    
931     /* -------------- set "logged" flag in notes and update them ------ */
932     llog->cache->notes->logged = TRUE;
933    
934     /* update flags in all copies of this cache */
935    
936     gpx_t *tgpx = appdata->gpx;
937     while(tgpx) {
938     cache_t *tcache = tgpx->cache;
939     while(tcache) {
940     if((tcache != llog->cache) &&
941     (strcmp(tcache->id, llog->cache->id) == 0)) {
942     printf("found dup cache %s in %s\n", tcache->id, tgpx->name);
943    
944     if(tcache->notes)
945     notes_free(tcache->notes);
946    
947     /* create a new note for this cache */
948     tcache->notes = malloc(sizeof(notes_t));
949     memset(tcache->notes, 0, sizeof(notes_t));
950     if(llog->cache->notes->text)
951     tcache->notes->text = strdup(llog->cache->notes->text);
952     tcache->notes->pos = llog->cache->notes->pos;
953     tcache->notes->override = llog->cache->notes->override;
954     tcache->notes->found = llog->cache->notes->found;
955     tcache->notes->logged = llog->cache->notes->logged;
956     tcache->notes->ftime = llog->cache->notes->ftime;
957     }
958     tcache = tcache->next;
959     }
960     tgpx = tgpx->next;
961     }
962    
963     /* finally write the notes file itself */
964     cache_context_t ccontext;
965     ccontext.appdata = appdata;
966     ccontext.cache = llog->cache;
967    
968 harbaum 133 notes_write_file(&ccontext,
969 harbaum 1 llog->cache->notes->text, llog->cache->notes->pos,
970     llog->cache->notes->override, llog->cache->notes->found,
971     llog->cache->notes->ftime, llog->cache->notes->logged);
972    
973     llog = llog->next;
974     }
975    
976     fclose(file);
977     }
978     } else {
979     /* restore old path, in case it has been altered but not been used */
980     free(appdata->fieldnotes_path);
981     appdata->fieldnotes_path = strdup(old_fieldnotes_path);
982     }
983    
984     gtk_widget_destroy(context.dialog);
985    
986     free(old_fieldnotes_path);
987    
988     /* free list */
989     while(log) {
990     log_chain_t *next = log->next;
991     g_free(log);
992     log = next;
993     }
994     }