Contents of /trunk/src/osm_api.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Tue Dec 9 20:06:06 2008 UTC (15 years, 5 months ago) by harbaum
Original Path: src/osm_api.c
File MIME type: text/plain
File size: 28131 byte(s)
Initial import
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