Contents of /trunk/src/notes.c

Parent Directory Parent Directory | Revision Log Revision Log


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