Contents of /trunk/src/osm_api.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (hide annotations)
Wed Dec 10 00:00:05 2008 UTC (15 years, 5 months ago) by achadwick
File MIME type: text/plain
File size: 28131 byte(s)
Begin trunk. No code changes.
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of OSM2Go.
5     *
6     * OSM2Go 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     * OSM2Go 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 OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include "appdata.h"
21    
22     #include <curl/curl.h>
23     #include <curl/types.h> /* new for v7 */
24     #include <curl/easy.h> /* new for v7 */
25     #include <unistd.h>
26    
27     static struct http_message_s {
28     int id;
29     char *msg;
30     } http_messages [] = {
31     { 200, "Ok" },
32     { 400, "Bad Request" },
33     { 401, "Unauthorized" },
34     { 403, "Forbidden" },
35     { 404, "Not Found" },
36     { 405, "Method Not Allowed" },
37     { 410, "Gone" },
38     { 412, "Precondition Failed" },
39     { 417, "(Expect rejected)" },
40     { 500, "Internal Server Error" },
41     { 503, "Service Unavailable" },
42     { 0, NULL }
43     };
44    
45     static char *osm_http_message(int id) {
46     struct http_message_s *msg = http_messages;
47    
48     while(msg->id) {
49     if(msg->id == id) return _(msg->msg);
50     msg++;
51     }
52    
53     return NULL;
54     }
55    
56     typedef struct {
57     GtkWidget *wait_dialog;
58     GtkWidget *pbar;
59     char *url, *filename;
60     gboolean cancelled;
61     CURLcode res;
62     long response;
63     char buffer[CURL_ERROR_SIZE];
64    
65     int percent;
66     } osm_download_context_t;
67    
68     typedef struct {
69     appdata_t *appdata;
70     GtkWidget *dialog;
71     osm_t *osm;
72     project_t *project;
73    
74     struct log_s {
75     GtkTextBuffer *buffer;
76     GtkWidget *view;
77     } log;
78    
79     } osm_upload_context_t;
80    
81     #if 0 // todo: figure out how to stop a curl transfer
82     /* Our usual callback function */
83     static void on_cancel(GtkWidget *widget, gpointer data) {
84     osm_download_context_t *context = (osm_download_context_t*)data;
85     context->cancelled = TRUE;
86     }
87     #endif
88    
89     static int my_progress_func(osm_download_context_t *context,
90     double t, /* dltotal */ double d, /* dlnow */
91     double ultotal, double ulnow) {
92     // printf("%f / %f (%g %%)\n", d, t, d*100.0/t);
93     if(t) context->percent = d*100.0/t;
94     else context->percent = 0;
95    
96     return 0;
97     }
98    
99     static void *my_thread(void *ptr) {
100     osm_download_context_t *context = (osm_download_context_t*)ptr;
101     CURL *curl;
102     FILE *outfile;
103    
104     curl = curl_easy_init();
105     if(curl) {
106     outfile = fopen(context->filename, "w");
107     if(outfile) {
108     curl_easy_setopt(curl, CURLOPT_URL, context->url);
109     curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
110     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
111     curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
112     curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, my_progress_func);
113     curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, context);
114     curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, context->buffer);
115    
116     context->res = curl_easy_perform(curl);
117     printf("curl perform returned with %d\n", context->res);
118    
119     curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &context->response);
120    
121     fclose(outfile);
122     }
123    
124     /* always cleanup */
125     curl_easy_cleanup(curl);
126     }
127    
128     printf("thread exiting\n");
129     context->percent = 100;
130    
131     return NULL;
132     }
133    
134     static gint wait_dialog_destroy_event(GtkWidget *widget, gpointer data) {
135     osm_download_context_t *context = (osm_download_context_t*)data;
136    
137     printf("destroying wait dialog\n");
138    
139     context->wait_dialog = NULL;
140    
141     /* todo: terminate http transfer!! */
142    
143     return FALSE;
144     }
145    
146     gboolean osm_download(GtkWidget *parent, project_t *project) {
147     printf("download ...\n");
148    
149     /* ------------ busy dialog -------------- */
150     osm_download_context_t *context = g_new0(osm_download_context_t, 1);
151     char minlon[G_ASCII_DTOSTR_BUF_SIZE], minlat[G_ASCII_DTOSTR_BUF_SIZE];
152     char maxlon[G_ASCII_DTOSTR_BUF_SIZE], maxlat[G_ASCII_DTOSTR_BUF_SIZE];
153    
154     g_ascii_dtostr(minlon, sizeof(minlon), project->min.lon);
155     g_ascii_dtostr(minlat, sizeof(minlat), project->min.lat);
156     g_ascii_dtostr(maxlon, sizeof(maxlon), project->max.lon);
157     g_ascii_dtostr(maxlat, sizeof(maxlat), project->max.lat);
158    
159     context->url = g_strdup_printf("%s/map?bbox=%s,%s,%s,%s",
160     project->server, minlon, minlat, maxlon, maxlat);
161    
162     context->filename = strdup(project->osm);
163     printf("going to fetch %s to %s\n", context->url, context->filename);
164    
165     context->wait_dialog = gtk_dialog_new();
166     gtk_dialog_set_has_separator(GTK_DIALOG(context->wait_dialog), FALSE);
167     gtk_window_set_title(GTK_WINDOW(context->wait_dialog), _("Downloading..."));
168     gtk_window_set_default_size(GTK_WINDOW(context->wait_dialog), 300, 10);
169    
170     gtk_window_set_modal(GTK_WINDOW(context->wait_dialog), TRUE);
171     gtk_window_set_transient_for(GTK_WINDOW(context->wait_dialog),
172     GTK_WINDOW(parent));
173     GtkAdjustment *adj = (GtkAdjustment*)gtk_adjustment_new(0, 0, 100, 0, 0, 0);
174     context->pbar = gtk_progress_bar_new_with_adjustment(adj);
175     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->wait_dialog)->vbox),
176     context->pbar);
177    
178     #if 0
179     GtkWidget *button = gtk_button_new_with_label(_("Cancel"));
180     gtk_signal_connect(GTK_OBJECT(button), "clicked",
181     GTK_SIGNAL_FUNC(on_cancel), (gpointer)context);
182     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(context->wait_dialog)->
183     action_area), button);
184     #endif
185    
186     gtk_signal_connect(GTK_OBJECT(context->wait_dialog),
187     "destroy", G_CALLBACK(wait_dialog_destroy_event), context);
188    
189     gtk_widget_show_all(context->wait_dialog);
190    
191     if(!g_thread_create(&my_thread, context, FALSE, NULL) != 0)
192     g_warning("can't create the thread");
193    
194     /* wait for download to finish */
195     int percent = -1;
196     while(context->wait_dialog) {
197     if(context->percent != percent) {
198     gtk_progress_set_value(GTK_PROGRESS(context->pbar),
199     context->percent);
200    
201     percent = context->percent;
202    
203     printf("context->percent = %d\n", context->percent);
204     if(percent == 100)
205     gtk_widget_destroy(context->wait_dialog);
206     } else
207     usleep(1000000);
208    
209     while(gtk_events_pending())
210     gtk_main_iteration();
211     }
212    
213     gboolean result = (context->res == 0);
214     if(!result)
215     errorf(parent, _("Download failed with message:\n\n%s"), context->buffer);
216    
217     if(context->response != 200) {
218     errorf(parent, _("Download failed with code %ld:\n\n%s\n"),
219     context->response, osm_http_message(context->response));
220     g_remove(context->filename);
221     }
222    
223     printf("clean up\n");
224     /* clean up */
225     if(context->url) g_free(context->url);
226     g_free(context);
227    
228     return result;
229     }
230    
231     typedef struct {
232     char *ptr;
233     int len;
234     } curl_data_t;
235    
236     static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
237     curl_data_t *p = (curl_data_t*)stream;
238    
239     // printf("request to read %d items of size %d, pointer = %p\n",
240     // nmemb, size, p->ptr);
241    
242     if(nmemb*size > p->len)
243     nmemb = p->len/size;
244    
245     memcpy(ptr, p->ptr, size*nmemb);
246     p->ptr += size*nmemb;
247     p->len -= size*nmemb;
248    
249     return nmemb;
250     }
251    
252     static size_t write_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
253     curl_data_t *p = (curl_data_t*)stream;
254    
255     p->ptr = g_realloc(p->ptr, p->len + size*nmemb + 1);
256     if(p->ptr) {
257     memcpy(p->ptr+p->len, ptr, size*nmemb);
258     p->len += size*nmemb;
259     p->ptr[p->len] = 0;
260     }
261     return nmemb;
262     }
263    
264     static void appendf(struct log_s *log, const char *fmt, ...) {
265     va_list args;
266     va_start( args, fmt );
267     char *buf = g_strdup_vprintf(fmt, args);
268     va_end( args );
269    
270     GtkTextIter end;
271     gtk_text_buffer_get_end_iter(log->buffer, &end);
272     gtk_text_buffer_insert(log->buffer, &end, buf, -1);
273    
274     g_free(buf);
275    
276     gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view),
277     &end, 0.0, FALSE, 0, 0);
278    
279     while(gtk_events_pending())
280     gtk_main_iteration();
281     }
282    
283     #define MAX_TRY 5
284    
285     static gboolean osm_update_item(struct log_s *log, char *xml_str,
286     char *url, char *user, item_id_t *id) {
287     int retry = MAX_TRY;
288     char buffer[CURL_ERROR_SIZE];
289    
290     CURL *curl;
291     CURLcode res;
292    
293     curl_data_t read_data;
294     curl_data_t write_data;
295    
296     while(retry >= 0) {
297    
298     if(retry != MAX_TRY)
299     appendf(log, _("Retry %d/%d "), MAX_TRY-retry, MAX_TRY-1);
300    
301     /* get a curl handle */
302     curl = curl_easy_init();
303     if(!curl) {
304     appendf(log, _("CURL init error\n"));
305     return FALSE;
306     }
307    
308     read_data.ptr = xml_str;
309     read_data.len = strlen(xml_str);
310     write_data.ptr = NULL;
311     write_data.len = 0;
312    
313     /* we want to use our own read/write functions */
314     curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
315     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
316    
317     /* enable uploading */
318     curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
319    
320     /* specify target URL, and note that this URL should include a file
321     name, not only a directory */
322     curl_easy_setopt(curl, CURLOPT_URL, url);
323    
324     /* now specify which file to upload */
325     curl_easy_setopt(curl, CURLOPT_READDATA, &read_data);
326    
327     /* provide the size of the upload, we specicially typecast the value
328     to curl_off_t since we must be sure to use the correct data size */
329     curl_easy_setopt(curl, CURLOPT_INFILESIZE, (long)strlen(xml_str));
330    
331     /* we pass our 'chunk' struct to the callback function */
332     curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_data);
333    
334     /* some servers don't like requests that are made without a user-agent
335     field, so we provide one */
336     curl_easy_setopt(curl, CURLOPT_USERAGENT,
337     PACKAGE "-libcurl/" VERSION);
338    
339     struct curl_slist *slist=NULL;
340     slist = curl_slist_append(slist, "Expect:");
341     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
342    
343     curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer);
344    
345     /* set user name and password for the authentication */
346     curl_easy_setopt(curl, CURLOPT_USERPWD, user);
347    
348     /* Now run off and do what you've been told! */
349     res = curl_easy_perform(curl);
350    
351     long response;
352     curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
353    
354     /* always cleanup */
355     curl_slist_free_all(slist);
356     curl_easy_cleanup(curl);
357    
358     printf("reply is \"%s\"\n", write_data.ptr);
359    
360     /* this will return the id on a successful create */
361     if(id && (res == 0) && (response == 200)) {
362     printf("request to parse successful reply as an id\n");
363     *id = strtoul(write_data.ptr, NULL, 10);
364     }
365    
366     g_free(write_data.ptr);
367    
368     if(res != 0)
369     appendf(log, _("failed: %s\n"), buffer);
370     else if(response != 200)
371     appendf(log, _("failed, code: %ld %s\n"),
372     response, osm_http_message(response));
373     else {
374     if(!id) appendf(log, _("ok\n"));
375     else appendf(log, _("ok: #%ld\n"), *id);
376     }
377    
378     /* don't retry unless we had an "internal server error" */
379     if(response != 500)
380     return((res == 0)&&(response == 200));
381    
382     retry--;
383     }
384    
385     return FALSE;
386     }
387    
388     static gboolean osm_delete_item(struct log_s *log, char *url, char *user) {
389     int retry = MAX_TRY;
390     char buffer[CURL_ERROR_SIZE];
391    
392     CURL *curl;
393     CURLcode res;
394    
395     while(retry >= 0) {
396    
397     if(retry != MAX_TRY)
398     appendf(log, _("Retry %d/%d "), MAX_TRY-retry, MAX_TRY-1);
399    
400     /* get a curl handle */
401     curl = curl_easy_init();
402     if(!curl) {
403     appendf(log, _("CURL init error\n"));
404     return FALSE;
405     }
406    
407     /* no read/write functions required */
408     curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
409    
410     /* specify target URL, and note that this URL should include a file
411     name, not only a directory */
412     curl_easy_setopt(curl, CURLOPT_URL, url);
413    
414     /* some servers don't like requests that are made without a user-agent
415     field, so we provide one */
416     curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "-libcurl/" VERSION);
417    
418     curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer);
419    
420     /* set user name and password for the authentication */
421     curl_easy_setopt(curl, CURLOPT_USERPWD, user);
422    
423     /* Now run off and do what you've been told! */
424     res = curl_easy_perform(curl);
425    
426     long response;
427     curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
428    
429     /* always cleanup */
430     curl_easy_cleanup(curl);
431    
432     if(res != 0)
433     appendf(log, _("failed: %s\n"), buffer);
434     else if(response != 200)
435     appendf(log, _("failed, code: %ld %s\n"),
436     response, osm_http_message(response));
437     else
438     appendf(log, _("ok\n"));
439    
440     /* don't retry unless we had an "internal server error" */
441     if(response != 500)
442     return((res == 0)&&(response == 200));
443    
444     retry--;
445     }
446    
447     return FALSE;
448     }
449    
450     typedef struct {
451     struct {
452     int total, new, dirty, deleted;
453     } ways, nodes, relations;
454     } osm_dirty_t;
455    
456     static GtkWidget *table_attach_label_c(GtkWidget *table, char *str,
457     int x1, int x2, int y1, int y2) {
458     GtkWidget *label = gtk_label_new(str);
459     gtk_table_attach_defaults(GTK_TABLE(table), label, x1, x2, y1, y2);
460     return label;
461     }
462    
463     static GtkWidget *table_attach_label_l(GtkWidget *table, char *str,
464     int x1, int x2, int y1, int y2) {
465     GtkWidget *label = table_attach_label_c(table, str, x1, x2, y1, y2);
466     gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
467     return label;
468     }
469    
470     static GtkWidget *table_attach_int(GtkWidget *table, int num,
471     int x1, int x2, int y1, int y2) {
472     char *str = g_strdup_printf("%d", num);
473     GtkWidget *label = table_attach_label_c(table, str, x1, x2, y1, y2);
474     g_free(str);
475     return label;
476     }
477    
478     static void osm_delete_nodes(osm_upload_context_t *context) {
479     node_t *node = context->osm->node;
480     project_t *project = context->project;
481    
482     while(node) {
483     /* make sure gui gets updated */
484     while(gtk_events_pending()) gtk_main_iteration();
485    
486     if(node->flags & OSM_FLAG_DELETED) {
487     printf("deleting node on server\n");
488    
489     appendf(&context->log, _("Delete node #%ld "), node->id);
490    
491     char *url = g_strdup_printf("%s/node/%lu", project->server, node->id);
492     char *cred = g_strdup_printf("%s:%s",
493     context->appdata->settings->username,
494     context->appdata->settings->password);
495    
496     if(osm_delete_item(&context->log, url, cred)) {
497     node->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED);
498     project->data_dirty = TRUE;
499     }
500    
501     g_free(cred);
502     }
503     node = node->next;
504     }
505     }
506    
507     static void osm_upload_nodes(osm_upload_context_t *context) {
508     node_t *node = context->osm->node;
509     project_t *project = context->project;
510    
511     while(node) {
512     /* make sure gui gets updated */
513     while(gtk_events_pending()) gtk_main_iteration();
514    
515     if((node->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
516     (!(node->flags & OSM_FLAG_DELETED))) {
517     char *url = NULL;
518    
519     if(node->flags & OSM_FLAG_NEW) {
520     url = g_strdup_printf("%s/node/create", project->server);
521     appendf(&context->log, _("New node "));
522     } else {
523     url = g_strdup_printf("%s/node/%lu", project->server, node->id);
524     appendf(&context->log, _("Modified node #%ld "), node->id);
525     }
526    
527     /* upload this node */
528     char *xml_str = osm_generate_xml_node(context->osm, node);
529     if(xml_str) {
530     printf("uploading node %s from address %p\n", url, xml_str);
531    
532     char *cred = g_strdup_printf("%s:%s",
533     context->appdata->settings->username, context->appdata->settings->password);
534     if(osm_update_item(&context->log, xml_str, url, cred,
535     (node->flags & OSM_FLAG_NEW)?&(node->id):NULL)) {
536    
537     node->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
538     project->data_dirty = TRUE;
539     }
540     g_free(cred);
541     }
542     g_free(url);
543     }
544     node = node->next;
545     }
546     }
547    
548     static void osm_delete_ways(osm_upload_context_t *context) {
549     way_t *way = context->osm->way;
550     project_t *project = context->project;
551    
552     while(way) {
553     /* make sure gui gets updated */
554     while(gtk_events_pending()) gtk_main_iteration();
555    
556     if(way->flags & OSM_FLAG_DELETED) {
557     printf("deleting way on server\n");
558    
559     appendf(&context->log, _("Delete way #%ld "), way->id);
560    
561     char *url = g_strdup_printf("%s/way/%lu", project->server, way->id);
562     char *cred = g_strdup_printf("%s:%s",
563     context->appdata->settings->username, context->appdata->settings->password);
564    
565     if(osm_delete_item(&context->log, url, cred)) {
566     way->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED);
567     project->data_dirty = TRUE;
568     }
569    
570     g_free(cred);
571     }
572     way = way->next;
573     }
574     }
575    
576    
577     static void osm_upload_ways(osm_upload_context_t *context) {
578     way_t *way = context->osm->way;
579     project_t *project = context->project;
580    
581     while(way) {
582     /* make sure gui gets updated */
583     while(gtk_events_pending()) gtk_main_iteration();
584    
585     if((way->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
586     (!(way->flags & OSM_FLAG_DELETED))) {
587     char *url = NULL;
588    
589     if(way->flags & OSM_FLAG_NEW) {
590     url = g_strdup_printf("%s/way/create", project->server);
591     appendf(&context->log, _("New way "));
592     } else {
593     url = g_strdup_printf("%s/way/%lu", project->server, way->id);
594     appendf(&context->log, _("Modified way #%ld "), way->id);
595     }
596    
597     /* upload this node */
598     char *xml_str = osm_generate_xml_way(context->osm, way);
599     if(xml_str) {
600     printf("uploading way %s from address %p\n", url, xml_str);
601    
602     char *cred = g_strdup_printf("%s:%s",
603     context->appdata->settings->username, context->appdata->settings->password);
604     if(osm_update_item(&context->log, xml_str, url, cred,
605     (way->flags & OSM_FLAG_NEW)?&(way->id):NULL)) {
606     way->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
607     project->data_dirty = TRUE;
608     }
609     g_free(cred);
610     }
611     g_free(url);
612     }
613     way = way->next;
614     }
615     }
616    
617    
618     static void osm_upload_relations(osm_upload_context_t *context) {
619     relation_t *relation = context->osm->relation;
620     project_t *project = context->project;
621    
622     while(relation) {
623     /* make sure gui gets updated */
624     while(gtk_events_pending()) gtk_main_iteration();
625    
626     if((relation->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
627     (!(relation->flags & OSM_FLAG_DELETED))) {
628     char *url = NULL;
629    
630     if(relation->flags & OSM_FLAG_NEW) {
631     url = g_strdup_printf("%s/relation/create", project->server);
632     appendf(&context->log, _("New relation "));
633     } else {
634     url = g_strdup_printf("%s/relation/%lu", project->server,relation->id);
635     appendf(&context->log, _("Modified relation #%ld "), relation->id);
636     }
637    
638     /* upload this relation */
639     char *xml_str = osm_generate_xml_relation(context->osm, relation);
640     if(xml_str) {
641     printf("uploading relation %s from address %p\n", url, xml_str);
642    
643     char *cred = g_strdup_printf("%s:%s",
644     context->appdata->settings->username, context->appdata->settings->password);
645     if(osm_update_item(&context->log, xml_str, url, cred,
646     (relation->flags & OSM_FLAG_NEW)?&(relation->id):NULL)) {
647     relation->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
648     project->data_dirty = TRUE;
649     }
650     g_free(cred);
651     }
652     g_free(url);
653     }
654     relation = relation->next;
655     }
656     }
657    
658    
659     void osm_upload(appdata_t *appdata, osm_t *osm, project_t *project) {
660    
661     printf("starting upload\n");
662    
663     /* upload config and confirmation dialog */
664    
665     /* count nodes */
666     osm_dirty_t dirty;
667     memset(&dirty, 0, sizeof(osm_dirty_t));
668    
669     node_t *node = osm->node;
670     while(node) {
671     dirty.nodes.total++;
672     if(node->flags & OSM_FLAG_DELETED) dirty.nodes.deleted++;
673     else if(node->flags & OSM_FLAG_NEW) dirty.nodes.new++;
674     else if(node->flags & OSM_FLAG_DIRTY) dirty.nodes.dirty++;
675    
676     node = node->next;
677     }
678     printf("nodes: new %2d, dirty %2d, deleted %2d\n",
679     dirty.nodes.new, dirty.nodes.dirty, dirty.nodes.deleted);
680    
681     /* count ways */
682     way_t *way = osm->way;
683     while(way) {
684     dirty.ways.total++;
685     if(way->flags & OSM_FLAG_DELETED) dirty.ways.deleted++;
686     else if(way->flags & OSM_FLAG_NEW) dirty.ways.new++;
687     else if(way->flags & OSM_FLAG_DIRTY) dirty.ways.dirty++;
688    
689     way = way->next;
690     }
691     printf("ways: new %2d, dirty %2d, deleted %2d\n",
692     dirty.ways.new, dirty.ways.dirty, dirty.ways.deleted);
693    
694     /* count relations */
695     relation_t *relation = osm->relation;
696     while(relation) {
697     dirty.relations.total++;
698     if(relation->flags & OSM_FLAG_DELETED) dirty.relations.deleted++;
699     else if(relation->flags & OSM_FLAG_NEW) dirty.relations.new++;
700     else if(relation->flags & OSM_FLAG_DIRTY) dirty.relations.dirty++;
701    
702     relation = relation->next;
703     }
704     printf("relations: new %2d, dirty %2d, deleted %2d\n",
705     dirty.relations.new, dirty.relations.dirty, dirty.relations.deleted);
706    
707    
708     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Upload to OSM"),
709     GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
710     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
711     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
712     NULL);
713    
714     GtkWidget *table = gtk_table_new(4, 5, TRUE);
715    
716     table_attach_label_c(table, _("Total"), 1, 2, 0, 1);
717     table_attach_label_c(table, _("New"), 2, 3, 0, 1);
718     table_attach_label_c(table, _("Modified"), 3, 4, 0, 1);
719     table_attach_label_c(table, _("Deleted"), 4, 5, 0, 1);
720    
721     table_attach_label_l(table, _("Nodes:"), 0, 1, 1, 2);
722     table_attach_int(table, dirty.nodes.total, 1, 2, 1, 2);
723     table_attach_int(table, dirty.nodes.new, 2, 3, 1, 2);
724     table_attach_int(table, dirty.nodes.dirty, 3, 4, 1, 2);
725     table_attach_int(table, dirty.nodes.deleted, 4, 5, 1, 2);
726    
727     table_attach_label_l(table, _("Ways:"), 0, 1, 2, 3);
728     table_attach_int(table, dirty.ways.total, 1, 2, 2, 3);
729     table_attach_int(table, dirty.ways.new, 2, 3, 2, 3);
730     table_attach_int(table, dirty.ways.dirty, 3, 4, 2, 3);
731     table_attach_int(table, dirty.ways.deleted, 4, 5, 2, 3);
732    
733     table_attach_label_l(table, _("Relations:"), 0, 1, 3, 4);
734     table_attach_int(table, dirty.relations.total, 1, 2, 3, 4);
735     table_attach_int(table, dirty.relations.new, 2, 3, 3, 4);
736     table_attach_int(table, dirty.relations.dirty, 3, 4, 3, 4);
737     table_attach_int(table, dirty.relations.deleted, 4, 5, 3, 4);
738    
739     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
740    
741     /* ------------------------------------------------------ */
742    
743     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
744     gtk_hseparator_new());
745    
746     /* ------- add username and password entries ------------ */
747    
748     table = gtk_table_new(2, 2, FALSE);
749     table_attach_label_l(table, _("Username:"), 0, 1, 0, 1);
750     GtkWidget *uentry = gtk_entry_new();
751     HILDON_ENTRY_NO_AUTOCAP(uentry);
752     gtk_entry_set_text(GTK_ENTRY(uentry), appdata->settings->username);
753     gtk_table_attach_defaults(GTK_TABLE(table), uentry, 1, 2, 0, 1);
754     table_attach_label_l(table, _("Password:"), 0, 1, 1, 2);
755     GtkWidget *pentry = gtk_entry_new();
756     HILDON_ENTRY_NO_AUTOCAP(pentry);
757     gtk_entry_set_text(GTK_ENTRY(pentry), appdata->settings->password);
758     gtk_entry_set_visibility(GTK_ENTRY(pentry), FALSE);
759     gtk_table_attach_defaults(GTK_TABLE(table), pentry, 1, 2, 1, 2);
760     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
761    
762     gtk_widget_show_all(dialog);
763    
764     if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
765     printf("upload cancelled\n");
766     gtk_widget_destroy(dialog);
767     return;
768     }
769    
770     printf("clicked ok\n");
771    
772     /* retrieve username and password */
773     if(appdata->settings->username)
774     g_free(appdata->settings->username);
775     appdata->settings->username =
776     g_strdup(gtk_entry_get_text(GTK_ENTRY(uentry)));
777    
778     if(appdata->settings->password)
779     g_free(appdata->settings->password);
780     appdata->settings->password =
781     g_strdup(gtk_entry_get_text(GTK_ENTRY(pentry)));
782    
783     gtk_widget_destroy(dialog);
784     project_save(GTK_WIDGET(appdata->window), project);
785    
786     /* osm upload itself also has a gui */
787     osm_upload_context_t *context = g_new0(osm_upload_context_t, 1);
788     context->appdata = appdata;
789     context->osm = osm;
790     context->project = project;
791    
792     context->dialog = gtk_dialog_new_with_buttons(_("Upload to OSM"),
793     GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
794     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
795     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
796     GTK_RESPONSE_CLOSE, FALSE);
797    
798     /* making the dialog a little wider makes it less "crowded" */
799     #ifndef USE_HILDON
800     gtk_window_set_default_size(GTK_WINDOW(context->dialog), 480, 256);
801     #else
802     gtk_window_set_default_size(GTK_WINDOW(context->dialog), 800, 480);
803     #endif
804    
805     /* ------- main ui elelent is this text view --------------- */
806    
807     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
808     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
809     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
810    
811     context->log.buffer = gtk_text_buffer_new(NULL);
812    
813     context->log.view = gtk_text_view_new_with_buffer(context->log.buffer);
814     gtk_text_view_set_editable(GTK_TEXT_VIEW(context->log.view), FALSE);
815     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(context->log.view), FALSE);
816    
817     gtk_container_add(GTK_CONTAINER(scrolled_window), context->log.view);
818     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
819     GTK_SHADOW_IN);
820    
821     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
822     scrolled_window);
823     gtk_widget_show_all(context->dialog);
824    
825     appendf(&context->log, _("Log generated by %s v%s using API 0.5\n"),
826     PACKAGE, VERSION);
827     appendf(&context->log, _("Uploading to %s\n"), project->server);
828    
829     /* check for dirty entries */
830     appendf(&context->log, _("Uploading nodes:\n"));
831     osm_upload_nodes(context);
832     appendf(&context->log, _("Uploading ways:\n"));
833     osm_upload_ways(context);
834     appendf(&context->log, _("Uploading relations:\n"));
835     osm_upload_relations(context);
836     appendf(&context->log, _("Deleting ways:\n"));
837     osm_delete_ways(context);
838     appendf(&context->log, _("Deleting nodes:\n"));
839     osm_delete_nodes(context);
840    
841    
842     appendf(&context->log, _("Upload done.\n"));
843    
844     gboolean reload_map = FALSE;
845     if(project->data_dirty) {
846     appendf(&context->log, _("Server data has been modified.\n"));
847     appendf(&context->log, _("Downloading updated osm data ...\n"));
848    
849     if(osm_download(context->dialog, project)) {
850     appendf(&context->log, _("Download successful!\n"));
851     appendf(&context->log, _("The map will be reloaded.\n"));
852     project->data_dirty = FALSE;
853     reload_map = TRUE;
854     } else
855     appendf(&context->log, _("Download failed!\n"));
856    
857     project_save(context->dialog, project);
858    
859     if(reload_map) {
860     /* this kind of rather brute force reload is useful as the moment */
861     /* after the upload is a nice moment to bring everything in sync again. */
862     /* we basically restart the entire map with fresh data from the server */
863     /* and the diff will hopefully be empty (if the upload was successful) */
864    
865     appendf(&context->log, _("Reloading map ...\n"));
866    
867     if(!diff_is_clean(appdata->osm, FALSE)) {
868     appendf(&context->log, _(">>>>>>>> DIFF IS NOT CLEAN <<<<<<<<\n"));
869     appendf(&context->log, _("Something went wrong during upload,\n"));
870     appendf(&context->log, _("proceed with care!\n"));
871     }
872    
873     /* redraw the entire map by destroying all map items and redrawing them */
874     appendf(&context->log, _("Cleaning up ...\n"));
875     diff_save(appdata->project, appdata->osm);
876     map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
877     osm_free(&appdata->icon, appdata->osm);
878    
879     appendf(&context->log, _("Loading OSM ...\n"));
880     appdata->osm = osm_parse(appdata->project->osm);
881     appendf(&context->log, _("Applying diff ...\n"));
882     diff_restore(appdata, appdata->project, appdata->osm);
883     appendf(&context->log, _("Painting ...\n"));
884     map_paint(appdata);
885     appendf(&context->log, _("Done!\n"));
886     }
887     }
888    
889     /* tell the user that he can stop waiting ... */
890     appendf(&context->log, _("Process finished.\n"));
891    
892     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
893     GTK_RESPONSE_CLOSE, TRUE);
894    
895     gtk_dialog_run(GTK_DIALOG(context->dialog));
896     gtk_widget_destroy(context->dialog);
897    
898     }
899