Parent Directory | Revision Log
Asynchronous network io
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 | appdata_t *appdata; |
58 | GtkWidget *dialog; |
59 | osm_t *osm; |
60 | project_t *project; |
61 | |
62 | struct log_s { |
63 | GtkTextBuffer *buffer; |
64 | GtkWidget *view; |
65 | } log; |
66 | |
67 | } osm_upload_context_t; |
68 | |
69 | gboolean osm_download(GtkWidget *parent, project_t *project) { |
70 | printf("download osm ...\n"); |
71 | |
72 | char minlon[G_ASCII_DTOSTR_BUF_SIZE], minlat[G_ASCII_DTOSTR_BUF_SIZE]; |
73 | char maxlon[G_ASCII_DTOSTR_BUF_SIZE], maxlat[G_ASCII_DTOSTR_BUF_SIZE]; |
74 | |
75 | g_ascii_dtostr(minlon, sizeof(minlon), project->min.lon); |
76 | g_ascii_dtostr(minlat, sizeof(minlat), project->min.lat); |
77 | g_ascii_dtostr(maxlon, sizeof(maxlon), project->max.lon); |
78 | g_ascii_dtostr(maxlat, sizeof(maxlat), project->max.lat); |
79 | |
80 | char *url = g_strdup_printf("%s/map?bbox=%s,%s,%s,%s", |
81 | project->server, minlon, minlat, maxlon, maxlat); |
82 | |
83 | gboolean result = net_io_download_file(parent, url, project->osm); |
84 | |
85 | g_free(url); |
86 | return result; |
87 | } |
88 | |
89 | typedef struct { |
90 | char *ptr; |
91 | int len; |
92 | } curl_data_t; |
93 | |
94 | static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) { |
95 | curl_data_t *p = (curl_data_t*)stream; |
96 | |
97 | // printf("request to read %d items of size %d, pointer = %p\n", |
98 | // nmemb, size, p->ptr); |
99 | |
100 | if(nmemb*size > p->len) |
101 | nmemb = p->len/size; |
102 | |
103 | memcpy(ptr, p->ptr, size*nmemb); |
104 | p->ptr += size*nmemb; |
105 | p->len -= size*nmemb; |
106 | |
107 | return nmemb; |
108 | } |
109 | |
110 | static size_t write_callback(void *ptr, size_t size, size_t nmemb, void *stream) { |
111 | curl_data_t *p = (curl_data_t*)stream; |
112 | |
113 | p->ptr = g_realloc(p->ptr, p->len + size*nmemb + 1); |
114 | if(p->ptr) { |
115 | memcpy(p->ptr+p->len, ptr, size*nmemb); |
116 | p->len += size*nmemb; |
117 | p->ptr[p->len] = 0; |
118 | } |
119 | return nmemb; |
120 | } |
121 | |
122 | static void appendf(struct log_s *log, const char *fmt, ...) { |
123 | va_list args; |
124 | va_start( args, fmt ); |
125 | char *buf = g_strdup_vprintf(fmt, args); |
126 | va_end( args ); |
127 | |
128 | GtkTextIter end; |
129 | gtk_text_buffer_get_end_iter(log->buffer, &end); |
130 | gtk_text_buffer_insert(log->buffer, &end, buf, -1); |
131 | |
132 | g_free(buf); |
133 | |
134 | gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view), |
135 | &end, 0.0, FALSE, 0, 0); |
136 | |
137 | while(gtk_events_pending()) |
138 | gtk_main_iteration(); |
139 | } |
140 | |
141 | #define MAX_TRY 5 |
142 | |
143 | static gboolean osm_update_item(struct log_s *log, char *xml_str, |
144 | char *url, char *user, item_id_t *id) { |
145 | int retry = MAX_TRY; |
146 | char buffer[CURL_ERROR_SIZE]; |
147 | |
148 | CURL *curl; |
149 | CURLcode res; |
150 | |
151 | curl_data_t read_data; |
152 | curl_data_t write_data; |
153 | |
154 | while(retry >= 0) { |
155 | |
156 | if(retry != MAX_TRY) |
157 | appendf(log, _("Retry %d/%d "), MAX_TRY-retry, MAX_TRY-1); |
158 | |
159 | /* get a curl handle */ |
160 | curl = curl_easy_init(); |
161 | if(!curl) { |
162 | appendf(log, _("CURL init error\n")); |
163 | return FALSE; |
164 | } |
165 | |
166 | read_data.ptr = xml_str; |
167 | read_data.len = strlen(xml_str); |
168 | write_data.ptr = NULL; |
169 | write_data.len = 0; |
170 | |
171 | /* we want to use our own read/write functions */ |
172 | curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); |
173 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); |
174 | |
175 | /* enable uploading */ |
176 | curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); |
177 | |
178 | /* specify target URL, and note that this URL should include a file |
179 | name, not only a directory */ |
180 | curl_easy_setopt(curl, CURLOPT_URL, url); |
181 | |
182 | /* now specify which file to upload */ |
183 | curl_easy_setopt(curl, CURLOPT_READDATA, &read_data); |
184 | |
185 | /* provide the size of the upload, we specicially typecast the value |
186 | to curl_off_t since we must be sure to use the correct data size */ |
187 | curl_easy_setopt(curl, CURLOPT_INFILESIZE, (long)strlen(xml_str)); |
188 | |
189 | /* we pass our 'chunk' struct to the callback function */ |
190 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_data); |
191 | |
192 | /* some servers don't like requests that are made without a user-agent |
193 | field, so we provide one */ |
194 | curl_easy_setopt(curl, CURLOPT_USERAGENT, |
195 | PACKAGE "-libcurl/" VERSION); |
196 | |
197 | struct curl_slist *slist=NULL; |
198 | slist = curl_slist_append(slist, "Expect:"); |
199 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); |
200 | |
201 | curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer); |
202 | |
203 | /* set user name and password for the authentication */ |
204 | curl_easy_setopt(curl, CURLOPT_USERPWD, user); |
205 | |
206 | /* Now run off and do what you've been told! */ |
207 | res = curl_easy_perform(curl); |
208 | |
209 | long response; |
210 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); |
211 | |
212 | /* always cleanup */ |
213 | curl_slist_free_all(slist); |
214 | curl_easy_cleanup(curl); |
215 | |
216 | printf("reply is \"%s\"\n", write_data.ptr); |
217 | |
218 | /* this will return the id on a successful create */ |
219 | if(id && (res == 0) && (response == 200)) { |
220 | printf("request to parse successful reply as an id\n"); |
221 | *id = strtoul(write_data.ptr, NULL, 10); |
222 | } |
223 | |
224 | g_free(write_data.ptr); |
225 | |
226 | if(res != 0) |
227 | appendf(log, _("failed: %s\n"), buffer); |
228 | else if(response != 200) |
229 | appendf(log, _("failed, code: %ld %s\n"), |
230 | response, osm_http_message(response)); |
231 | else { |
232 | if(!id) appendf(log, _("ok\n")); |
233 | else appendf(log, _("ok: #%ld\n"), *id); |
234 | } |
235 | |
236 | /* don't retry unless we had an "internal server error" */ |
237 | if(response != 500) |
238 | return((res == 0)&&(response == 200)); |
239 | |
240 | retry--; |
241 | } |
242 | |
243 | return FALSE; |
244 | } |
245 | |
246 | static gboolean osm_delete_item(struct log_s *log, char *url, char *user) { |
247 | int retry = MAX_TRY; |
248 | char buffer[CURL_ERROR_SIZE]; |
249 | |
250 | CURL *curl; |
251 | CURLcode res; |
252 | |
253 | while(retry >= 0) { |
254 | |
255 | if(retry != MAX_TRY) |
256 | appendf(log, _("Retry %d/%d "), MAX_TRY-retry, MAX_TRY-1); |
257 | |
258 | /* get a curl handle */ |
259 | curl = curl_easy_init(); |
260 | if(!curl) { |
261 | appendf(log, _("CURL init error\n")); |
262 | return FALSE; |
263 | } |
264 | |
265 | /* no read/write functions required */ |
266 | curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); |
267 | |
268 | /* specify target URL, and note that this URL should include a file |
269 | name, not only a directory */ |
270 | curl_easy_setopt(curl, CURLOPT_URL, url); |
271 | |
272 | /* some servers don't like requests that are made without a user-agent |
273 | field, so we provide one */ |
274 | curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "-libcurl/" VERSION); |
275 | |
276 | curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer); |
277 | |
278 | /* set user name and password for the authentication */ |
279 | curl_easy_setopt(curl, CURLOPT_USERPWD, user); |
280 | |
281 | /* Now run off and do what you've been told! */ |
282 | res = curl_easy_perform(curl); |
283 | |
284 | long response; |
285 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); |
286 | |
287 | /* always cleanup */ |
288 | curl_easy_cleanup(curl); |
289 | |
290 | if(res != 0) |
291 | appendf(log, _("failed: %s\n"), buffer); |
292 | else if(response != 200) |
293 | appendf(log, _("failed, code: %ld %s\n"), |
294 | response, osm_http_message(response)); |
295 | else |
296 | appendf(log, _("ok\n")); |
297 | |
298 | /* don't retry unless we had an "internal server error" */ |
299 | if(response != 500) |
300 | return((res == 0)&&(response == 200)); |
301 | |
302 | retry--; |
303 | } |
304 | |
305 | return FALSE; |
306 | } |
307 | |
308 | typedef struct { |
309 | struct { |
310 | int total, new, dirty, deleted; |
311 | } ways, nodes, relations; |
312 | } osm_dirty_t; |
313 | |
314 | static GtkWidget *table_attach_label_c(GtkWidget *table, char *str, |
315 | int x1, int x2, int y1, int y2) { |
316 | GtkWidget *label = gtk_label_new(str); |
317 | gtk_table_attach_defaults(GTK_TABLE(table), label, x1, x2, y1, y2); |
318 | return label; |
319 | } |
320 | |
321 | static GtkWidget *table_attach_label_l(GtkWidget *table, char *str, |
322 | int x1, int x2, int y1, int y2) { |
323 | GtkWidget *label = table_attach_label_c(table, str, x1, x2, y1, y2); |
324 | gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f); |
325 | return label; |
326 | } |
327 | |
328 | static GtkWidget *table_attach_int(GtkWidget *table, int num, |
329 | int x1, int x2, int y1, int y2) { |
330 | char *str = g_strdup_printf("%d", num); |
331 | GtkWidget *label = table_attach_label_c(table, str, x1, x2, y1, y2); |
332 | g_free(str); |
333 | return label; |
334 | } |
335 | |
336 | static void osm_delete_nodes(osm_upload_context_t *context) { |
337 | node_t *node = context->osm->node; |
338 | project_t *project = context->project; |
339 | |
340 | while(node) { |
341 | /* make sure gui gets updated */ |
342 | while(gtk_events_pending()) gtk_main_iteration(); |
343 | |
344 | if(node->flags & OSM_FLAG_DELETED) { |
345 | printf("deleting node on server\n"); |
346 | |
347 | appendf(&context->log, _("Delete node #%ld "), node->id); |
348 | |
349 | char *url = g_strdup_printf("%s/node/%lu", project->server, node->id); |
350 | char *cred = g_strdup_printf("%s:%s", |
351 | context->appdata->settings->username, |
352 | context->appdata->settings->password); |
353 | |
354 | if(osm_delete_item(&context->log, url, cred)) { |
355 | node->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED); |
356 | project->data_dirty = TRUE; |
357 | } |
358 | |
359 | g_free(cred); |
360 | } |
361 | node = node->next; |
362 | } |
363 | } |
364 | |
365 | static void osm_upload_nodes(osm_upload_context_t *context) { |
366 | node_t *node = context->osm->node; |
367 | project_t *project = context->project; |
368 | |
369 | while(node) { |
370 | /* make sure gui gets updated */ |
371 | while(gtk_events_pending()) gtk_main_iteration(); |
372 | |
373 | if((node->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) && |
374 | (!(node->flags & OSM_FLAG_DELETED))) { |
375 | char *url = NULL; |
376 | |
377 | if(node->flags & OSM_FLAG_NEW) { |
378 | url = g_strdup_printf("%s/node/create", project->server); |
379 | appendf(&context->log, _("New node ")); |
380 | } else { |
381 | url = g_strdup_printf("%s/node/%lu", project->server, node->id); |
382 | appendf(&context->log, _("Modified node #%ld "), node->id); |
383 | } |
384 | |
385 | /* upload this node */ |
386 | char *xml_str = osm_generate_xml_node(context->osm, node); |
387 | if(xml_str) { |
388 | printf("uploading node %s from address %p\n", url, xml_str); |
389 | |
390 | char *cred = g_strdup_printf("%s:%s", |
391 | context->appdata->settings->username, context->appdata->settings->password); |
392 | if(osm_update_item(&context->log, xml_str, url, cred, |
393 | (node->flags & OSM_FLAG_NEW)?&(node->id):NULL)) { |
394 | |
395 | node->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW); |
396 | project->data_dirty = TRUE; |
397 | } |
398 | g_free(cred); |
399 | } |
400 | g_free(url); |
401 | } |
402 | node = node->next; |
403 | } |
404 | } |
405 | |
406 | static void osm_delete_ways(osm_upload_context_t *context) { |
407 | way_t *way = context->osm->way; |
408 | project_t *project = context->project; |
409 | |
410 | while(way) { |
411 | /* make sure gui gets updated */ |
412 | while(gtk_events_pending()) gtk_main_iteration(); |
413 | |
414 | if(way->flags & OSM_FLAG_DELETED) { |
415 | printf("deleting way on server\n"); |
416 | |
417 | appendf(&context->log, _("Delete way #%ld "), way->id); |
418 | |
419 | char *url = g_strdup_printf("%s/way/%lu", project->server, way->id); |
420 | char *cred = g_strdup_printf("%s:%s", |
421 | context->appdata->settings->username, context->appdata->settings->password); |
422 | |
423 | if(osm_delete_item(&context->log, url, cred)) { |
424 | way->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED); |
425 | project->data_dirty = TRUE; |
426 | } |
427 | |
428 | g_free(cred); |
429 | } |
430 | way = way->next; |
431 | } |
432 | } |
433 | |
434 | |
435 | static void osm_upload_ways(osm_upload_context_t *context) { |
436 | way_t *way = context->osm->way; |
437 | project_t *project = context->project; |
438 | |
439 | while(way) { |
440 | /* make sure gui gets updated */ |
441 | while(gtk_events_pending()) gtk_main_iteration(); |
442 | |
443 | if((way->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) && |
444 | (!(way->flags & OSM_FLAG_DELETED))) { |
445 | char *url = NULL; |
446 | |
447 | if(way->flags & OSM_FLAG_NEW) { |
448 | url = g_strdup_printf("%s/way/create", project->server); |
449 | appendf(&context->log, _("New way ")); |
450 | } else { |
451 | url = g_strdup_printf("%s/way/%lu", project->server, way->id); |
452 | appendf(&context->log, _("Modified way #%ld "), way->id); |
453 | } |
454 | |
455 | /* upload this node */ |
456 | char *xml_str = osm_generate_xml_way(context->osm, way); |
457 | if(xml_str) { |
458 | printf("uploading way %s from address %p\n", url, xml_str); |
459 | |
460 | char *cred = g_strdup_printf("%s:%s", |
461 | context->appdata->settings->username, context->appdata->settings->password); |
462 | if(osm_update_item(&context->log, xml_str, url, cred, |
463 | (way->flags & OSM_FLAG_NEW)?&(way->id):NULL)) { |
464 | way->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW); |
465 | project->data_dirty = TRUE; |
466 | } |
467 | g_free(cred); |
468 | } |
469 | g_free(url); |
470 | } |
471 | way = way->next; |
472 | } |
473 | } |
474 | |
475 | |
476 | static void osm_upload_relations(osm_upload_context_t *context) { |
477 | relation_t *relation = context->osm->relation; |
478 | project_t *project = context->project; |
479 | |
480 | while(relation) { |
481 | /* make sure gui gets updated */ |
482 | while(gtk_events_pending()) gtk_main_iteration(); |
483 | |
484 | if((relation->flags & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) && |
485 | (!(relation->flags & OSM_FLAG_DELETED))) { |
486 | char *url = NULL; |
487 | |
488 | if(relation->flags & OSM_FLAG_NEW) { |
489 | url = g_strdup_printf("%s/relation/create", project->server); |
490 | appendf(&context->log, _("New relation ")); |
491 | } else { |
492 | url = g_strdup_printf("%s/relation/%lu", project->server,relation->id); |
493 | appendf(&context->log, _("Modified relation #%ld "), relation->id); |
494 | } |
495 | |
496 | /* upload this relation */ |
497 | char *xml_str = osm_generate_xml_relation(context->osm, relation); |
498 | if(xml_str) { |
499 | printf("uploading relation %s from address %p\n", url, xml_str); |
500 | |
501 | char *cred = g_strdup_printf("%s:%s", |
502 | context->appdata->settings->username, context->appdata->settings->password); |
503 | if(osm_update_item(&context->log, xml_str, url, cred, |
504 | (relation->flags & OSM_FLAG_NEW)?&(relation->id):NULL)) { |
505 | relation->flags &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW); |
506 | project->data_dirty = TRUE; |
507 | } |
508 | g_free(cred); |
509 | } |
510 | g_free(url); |
511 | } |
512 | relation = relation->next; |
513 | } |
514 | } |
515 | |
516 | |
517 | void osm_upload(appdata_t *appdata, osm_t *osm, project_t *project) { |
518 | |
519 | printf("starting upload\n"); |
520 | |
521 | /* upload config and confirmation dialog */ |
522 | |
523 | /* count nodes */ |
524 | osm_dirty_t dirty; |
525 | memset(&dirty, 0, sizeof(osm_dirty_t)); |
526 | |
527 | node_t *node = osm->node; |
528 | while(node) { |
529 | dirty.nodes.total++; |
530 | if(node->flags & OSM_FLAG_DELETED) dirty.nodes.deleted++; |
531 | else if(node->flags & OSM_FLAG_NEW) dirty.nodes.new++; |
532 | else if(node->flags & OSM_FLAG_DIRTY) dirty.nodes.dirty++; |
533 | |
534 | node = node->next; |
535 | } |
536 | printf("nodes: new %2d, dirty %2d, deleted %2d\n", |
537 | dirty.nodes.new, dirty.nodes.dirty, dirty.nodes.deleted); |
538 | |
539 | /* count ways */ |
540 | way_t *way = osm->way; |
541 | while(way) { |
542 | dirty.ways.total++; |
543 | if(way->flags & OSM_FLAG_DELETED) dirty.ways.deleted++; |
544 | else if(way->flags & OSM_FLAG_NEW) dirty.ways.new++; |
545 | else if(way->flags & OSM_FLAG_DIRTY) dirty.ways.dirty++; |
546 | |
547 | way = way->next; |
548 | } |
549 | printf("ways: new %2d, dirty %2d, deleted %2d\n", |
550 | dirty.ways.new, dirty.ways.dirty, dirty.ways.deleted); |
551 | |
552 | /* count relations */ |
553 | relation_t *relation = osm->relation; |
554 | while(relation) { |
555 | dirty.relations.total++; |
556 | if(relation->flags & OSM_FLAG_DELETED) dirty.relations.deleted++; |
557 | else if(relation->flags & OSM_FLAG_NEW) dirty.relations.new++; |
558 | else if(relation->flags & OSM_FLAG_DIRTY) dirty.relations.dirty++; |
559 | |
560 | relation = relation->next; |
561 | } |
562 | printf("relations: new %2d, dirty %2d, deleted %2d\n", |
563 | dirty.relations.new, dirty.relations.dirty, dirty.relations.deleted); |
564 | |
565 | |
566 | GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Upload to OSM"), |
567 | GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL, |
568 | GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, |
569 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, |
570 | NULL); |
571 | |
572 | GtkWidget *table = gtk_table_new(4, 5, TRUE); |
573 | |
574 | table_attach_label_c(table, _("Total"), 1, 2, 0, 1); |
575 | table_attach_label_c(table, _("New"), 2, 3, 0, 1); |
576 | table_attach_label_c(table, _("Modified"), 3, 4, 0, 1); |
577 | table_attach_label_c(table, _("Deleted"), 4, 5, 0, 1); |
578 | |
579 | table_attach_label_l(table, _("Nodes:"), 0, 1, 1, 2); |
580 | table_attach_int(table, dirty.nodes.total, 1, 2, 1, 2); |
581 | table_attach_int(table, dirty.nodes.new, 2, 3, 1, 2); |
582 | table_attach_int(table, dirty.nodes.dirty, 3, 4, 1, 2); |
583 | table_attach_int(table, dirty.nodes.deleted, 4, 5, 1, 2); |
584 | |
585 | table_attach_label_l(table, _("Ways:"), 0, 1, 2, 3); |
586 | table_attach_int(table, dirty.ways.total, 1, 2, 2, 3); |
587 | table_attach_int(table, dirty.ways.new, 2, 3, 2, 3); |
588 | table_attach_int(table, dirty.ways.dirty, 3, 4, 2, 3); |
589 | table_attach_int(table, dirty.ways.deleted, 4, 5, 2, 3); |
590 | |
591 | table_attach_label_l(table, _("Relations:"), 0, 1, 3, 4); |
592 | table_attach_int(table, dirty.relations.total, 1, 2, 3, 4); |
593 | table_attach_int(table, dirty.relations.new, 2, 3, 3, 4); |
594 | table_attach_int(table, dirty.relations.dirty, 3, 4, 3, 4); |
595 | table_attach_int(table, dirty.relations.deleted, 4, 5, 3, 4); |
596 | |
597 | gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table); |
598 | |
599 | /* ------------------------------------------------------ */ |
600 | |
601 | gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), |
602 | gtk_hseparator_new()); |
603 | |
604 | /* ------- add username and password entries ------------ */ |
605 | |
606 | table = gtk_table_new(2, 2, FALSE); |
607 | table_attach_label_l(table, _("Username:"), 0, 1, 0, 1); |
608 | GtkWidget *uentry = gtk_entry_new(); |
609 | HILDON_ENTRY_NO_AUTOCAP(uentry); |
610 | gtk_entry_set_text(GTK_ENTRY(uentry), appdata->settings->username); |
611 | gtk_table_attach_defaults(GTK_TABLE(table), uentry, 1, 2, 0, 1); |
612 | table_attach_label_l(table, _("Password:"), 0, 1, 1, 2); |
613 | GtkWidget *pentry = gtk_entry_new(); |
614 | HILDON_ENTRY_NO_AUTOCAP(pentry); |
615 | gtk_entry_set_text(GTK_ENTRY(pentry), appdata->settings->password); |
616 | gtk_entry_set_visibility(GTK_ENTRY(pentry), FALSE); |
617 | gtk_table_attach_defaults(GTK_TABLE(table), pentry, 1, 2, 1, 2); |
618 | gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table); |
619 | |
620 | gtk_widget_show_all(dialog); |
621 | |
622 | if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) { |
623 | printf("upload cancelled\n"); |
624 | gtk_widget_destroy(dialog); |
625 | return; |
626 | } |
627 | |
628 | printf("clicked ok\n"); |
629 | |
630 | /* retrieve username and password */ |
631 | if(appdata->settings->username) |
632 | g_free(appdata->settings->username); |
633 | appdata->settings->username = |
634 | g_strdup(gtk_entry_get_text(GTK_ENTRY(uentry))); |
635 | |
636 | if(appdata->settings->password) |
637 | g_free(appdata->settings->password); |
638 | appdata->settings->password = |
639 | g_strdup(gtk_entry_get_text(GTK_ENTRY(pentry))); |
640 | |
641 | gtk_widget_destroy(dialog); |
642 | project_save(GTK_WIDGET(appdata->window), project); |
643 | |
644 | /* osm upload itself also has a gui */ |
645 | osm_upload_context_t *context = g_new0(osm_upload_context_t, 1); |
646 | context->appdata = appdata; |
647 | context->osm = osm; |
648 | context->project = project; |
649 | |
650 | context->dialog = gtk_dialog_new_with_buttons(_("Upload to OSM"), |
651 | GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL, |
652 | GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); |
653 | gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog), |
654 | GTK_RESPONSE_CLOSE, FALSE); |
655 | |
656 | /* making the dialog a little wider makes it less "crowded" */ |
657 | #ifndef USE_HILDON |
658 | gtk_window_set_default_size(GTK_WINDOW(context->dialog), 480, 256); |
659 | #else |
660 | gtk_window_set_default_size(GTK_WINDOW(context->dialog), 800, 480); |
661 | #endif |
662 | |
663 | /* ------- main ui elelent is this text view --------------- */ |
664 | |
665 | GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL); |
666 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), |
667 | GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
668 | |
669 | context->log.buffer = gtk_text_buffer_new(NULL); |
670 | |
671 | context->log.view = gtk_text_view_new_with_buffer(context->log.buffer); |
672 | gtk_text_view_set_editable(GTK_TEXT_VIEW(context->log.view), FALSE); |
673 | gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(context->log.view), FALSE); |
674 | |
675 | gtk_container_add(GTK_CONTAINER(scrolled_window), context->log.view); |
676 | gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), |
677 | GTK_SHADOW_IN); |
678 | |
679 | gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), |
680 | scrolled_window); |
681 | gtk_widget_show_all(context->dialog); |
682 | |
683 | appendf(&context->log, _("Log generated by %s v%s using API 0.5\n"), |
684 | PACKAGE, VERSION); |
685 | appendf(&context->log, _("Uploading to %s\n"), project->server); |
686 | |
687 | /* check for dirty entries */ |
688 | appendf(&context->log, _("Uploading nodes:\n")); |
689 | osm_upload_nodes(context); |
690 | appendf(&context->log, _("Uploading ways:\n")); |
691 | osm_upload_ways(context); |
692 | appendf(&context->log, _("Uploading relations:\n")); |
693 | osm_upload_relations(context); |
694 | appendf(&context->log, _("Deleting ways:\n")); |
695 | osm_delete_ways(context); |
696 | appendf(&context->log, _("Deleting nodes:\n")); |
697 | osm_delete_nodes(context); |
698 | |
699 | |
700 | appendf(&context->log, _("Upload done.\n")); |
701 | |
702 | gboolean reload_map = FALSE; |
703 | if(project->data_dirty) { |
704 | appendf(&context->log, _("Server data has been modified.\n")); |
705 | appendf(&context->log, _("Downloading updated osm data ...\n")); |
706 | |
707 | if(osm_download(context->dialog, project)) { |
708 | appendf(&context->log, _("Download successful!\n")); |
709 | appendf(&context->log, _("The map will be reloaded.\n")); |
710 | project->data_dirty = FALSE; |
711 | reload_map = TRUE; |
712 | } else |
713 | appendf(&context->log, _("Download failed!\n")); |
714 | |
715 | project_save(context->dialog, project); |
716 | |
717 | if(reload_map) { |
718 | /* this kind of rather brute force reload is useful as the moment */ |
719 | /* after the upload is a nice moment to bring everything in sync again. */ |
720 | /* we basically restart the entire map with fresh data from the server */ |
721 | /* and the diff will hopefully be empty (if the upload was successful) */ |
722 | |
723 | appendf(&context->log, _("Reloading map ...\n")); |
724 | |
725 | if(!diff_is_clean(appdata->osm, FALSE)) { |
726 | appendf(&context->log, _(">>>>>>>> DIFF IS NOT CLEAN <<<<<<<<\n")); |
727 | appendf(&context->log, _("Something went wrong during upload,\n")); |
728 | appendf(&context->log, _("proceed with care!\n")); |
729 | } |
730 | |
731 | /* redraw the entire map by destroying all map items and redrawing them */ |
732 | appendf(&context->log, _("Cleaning up ...\n")); |
733 | diff_save(appdata->project, appdata->osm); |
734 | map_clear(appdata, MAP_LAYER_OBJECTS_ONLY); |
735 | osm_free(&appdata->icon, appdata->osm); |
736 | |
737 | appendf(&context->log, _("Loading OSM ...\n")); |
738 | appdata->osm = osm_parse(appdata->project->osm); |
739 | appendf(&context->log, _("Applying diff ...\n")); |
740 | diff_restore(appdata, appdata->project, appdata->osm); |
741 | appendf(&context->log, _("Painting ...\n")); |
742 | map_paint(appdata); |
743 | appendf(&context->log, _("Done!\n")); |
744 | } |
745 | } |
746 | |
747 | /* tell the user that he can stop waiting ... */ |
748 | appendf(&context->log, _("Process finished.\n")); |
749 | |
750 | gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog), |
751 | GTK_RESPONSE_CLOSE, TRUE); |
752 | |
753 | gtk_dialog_run(GTK_DIALOG(context->dialog)); |
754 | gtk_widget_destroy(context->dialog); |
755 | |
756 | } |
757 |