1 /**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 * Copyright 2008 OpenGeo.org
6 * Copyright 2010 LISAsoft
7 *
8 * This is free software; you can redistribute and/or modify it under
9 * the terms of the GNU General Public Licence. See the COPYING file.
10 *
11 * Maintainer: Paul Ramsey <pramsey@cleverelephant.ca>
12 * Regina Obe <lr@pcorp.us>
13 * Mark Leslie <mark.leslie@lisasoft.com>
14 *
15 **********************************************************************/
16
17 #include "../postgis_config.h"
18
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdk.h>
25 #include <sys/stat.h>
26 #include "libpq-fe.h"
27 #include "shp2pgsql-core.h"
28 #include "pgsql2shp-core.h"
29
30 #define GUI_RCSID "shp2pgsql-gui $Revision$"
31 #define SHAPEFIELDMAXWIDTH 60
32
33 static void pgui_log_va(const char *fmt, va_list ap);
34 static void pgui_seterr_va(const char *fmt, va_list ap);
35
36 static void update_conn_ui_from_conn_config(void);
37
38 /* If GTK+ version is < 2.14.0, define gtk_dialog_get_content_area() */
39 #if !GTK_CHECK_VERSION(2, 14, 0)
40 #if !defined(gtk_dialog_get_content_area)
41 #define gtk_dialog_get_content_area(dialog) GTK_DIALOG(dialog)->vbox
42 #endif
43 #endif
44
45 /*
46 ** Global variables for GUI only
47 */
48
49 /* Main window */
50 static GtkWidget *window_main = NULL;
51
52 static GtkWidget *textview_log = NULL;
53 static GtkTextBuffer *textbuffer_log = NULL;
54
55 /* Main import window (listview) */
56 GtkListStore *import_file_list_store;
57 GtkWidget *import_tree;
58 GtkCellRenderer *import_filename_renderer;
59 GtkCellRenderer *import_schema_renderer;
60 GtkCellRenderer *import_table_renderer;
61 GtkCellRenderer *import_geom_column_renderer;
62 GtkCellRenderer *import_srid_renderer;
63 GtkCellRenderer *import_mode_renderer;
64 GtkCellRenderer *import_remove_renderer;
65
66 GtkTreeViewColumn *import_filename_column;
67 GtkTreeViewColumn *import_schema_column;
68 GtkTreeViewColumn *import_table_column;
69 GtkTreeViewColumn *import_geom_column;
70 GtkTreeViewColumn *import_srid_column;
71 GtkTreeViewColumn *import_mode_column;
72 GtkTreeViewColumn *import_remove_column;
73
74 static GtkWidget *add_file_button = NULL;
75
76 GtkWidget *loader_mode_combo = NULL;
77 GtkListStore *loader_mode_combo_list;
78
79 /* Main export window (listview) */
80 GtkListStore *export_table_list_store;
81 GtkWidget *export_tree;
82 GtkWidget *export_geom_column_combo;
83 GtkCellRenderer *export_schema_renderer;
84 GtkCellRenderer *export_table_renderer;
85 GtkCellRenderer *export_geom_column_renderer;
86 GtkCellRenderer *export_filename_renderer;
87 GtkCellRenderer *export_remove_renderer;
88
89 GtkTreeViewColumn *export_schema_column;
90 GtkTreeViewColumn *export_table_column;
91 GtkTreeViewColumn *export_geom_column;
92 GtkTreeViewColumn *export_filename_column;
93 GtkTreeViewColumn *export_remove_column;
94
95 static GtkWidget *add_table_button = NULL;
96
97 /* PostgreSQL database connection window */
98 static GtkWidget *window_conn = NULL;
99
100 static GtkWidget *entry_pg_user = NULL;
101 static GtkWidget *entry_pg_pass = NULL;
102 static GtkWidget *entry_pg_host = NULL;
103 static GtkWidget *entry_pg_port = NULL;
104 static GtkWidget *entry_pg_db = NULL;
105
106 /* Loader options window */
107 static GtkWidget *dialog_loader_options = NULL;
108 static GtkWidget *entry_options_encoding = NULL;
109 static GtkWidget *checkbutton_loader_options_preservecase = NULL;
110 static GtkWidget *checkbutton_loader_options_forceint = NULL;
111 static GtkWidget *checkbutton_loader_options_autoindex = NULL;
112 static GtkWidget *checkbutton_loader_options_dbfonly = NULL;
113 static GtkWidget *checkbutton_loader_options_dumpformat = NULL;
114 static GtkWidget *checkbutton_loader_options_geography = NULL;
115 static GtkWidget *checkbutton_loader_options_simplegeoms = NULL;
116
117 /* Dumper options window */
118 static GtkWidget *dialog_dumper_options = NULL;
119 static GtkWidget *checkbutton_dumper_options_includegid = NULL;
120 static GtkWidget *checkbutton_dumper_options_keep_fieldname_case = NULL;
121 static GtkWidget *checkbutton_dumper_options_unescapedattrs = NULL;
122
123 /* About dialog */
124 static GtkWidget *dialog_about = NULL;
125
126 /* File chooser */
127 static GtkWidget *dialog_filechooser = NULL;
128 static GtkWidget *dialog_folderchooser = NULL;
129
130 /* Progress dialog */
131 static GtkWidget *dialog_progress = NULL;
132 static GtkWidget *progress = NULL;
133 static GtkWidget *label_progress = NULL;
134
135 /* Table chooser dialog */
136 static GtkWidget *dialog_tablechooser = NULL;
137 GtkListStore *chooser_filtered_table_list_store;
138 GtkListStore *chooser_table_list_store;
139 GtkWidget *chooser_tree;
140 GtkCellRenderer *chooser_schema_renderer;
141 GtkCellRenderer *chooser_table_renderer;
142 GtkTreeViewColumn *chooser_schema_column;
143 GtkTreeViewColumn *chooser_table_column;
144 static GtkWidget *checkbutton_chooser_geoonly = NULL;
145
146 /* Other items */
147 static int valid_connection = 0;
148
149 /* Constants for the list view etc. */
150 enum
151 {
152 IMPORT_POINTER_COLUMN,
153 IMPORT_FILENAME_COLUMN,
154 IMPORT_SCHEMA_COLUMN,
155 IMPORT_TABLE_COLUMN,
156 IMPORT_GEOMETRY_COLUMN,
157 IMPORT_SRID_COLUMN,
158 IMPORT_MODE_COLUMN,
159 IMPORT_REMOVE_COLUMN,
160 IMPORT_N_COLUMNS
161 };
162
163 enum
164 {
165 LOADER_MODE_COMBO_TEXT,
166 LOADER_MODE_COMBO_OPTION_CHAR,
167 LOADER_MODE_COMBO_COLUMNS
168 };
169
170 enum
171 {
172 CREATE_MODE,
173 APPEND_MODE,
174 DELETE_MODE,
175 PREPARE_MODE
176 };
177
178 enum
179 {
180 EXPORT_POINTER_COLUMN,
181 EXPORT_SCHEMA_COLUMN,
182 EXPORT_TABLE_COLUMN,
183 EXPORT_GEOMETRY_COLUMN,
184 EXPORT_GEOMETRY_LISTSTORE_COLUMN,
185 EXPORT_FILENAME_COLUMN,
186 EXPORT_REMOVE_COLUMN,
187 EXPORT_N_COLUMNS
188 };
189
190 enum
191 {
192 TABLECHOOSER_SCHEMA_COLUMN,
193 TABLECHOOSER_TABLE_COLUMN,
194 TABLECHOOSER_GEO_LISTSTORE_COLUMN,
195 TABLECHOOSER_GEO_COLUMN,
196 TABLECHOOSER_HASGEO_COLUMN,
197 TABLECHOOSER_N_COLUMNS
198 };
199
200 enum
201 {
202 TABLECHOOSER_GEOCOL_COMBO_TEXT,
203 TABLECHOOSER_GEOCOL_COMBO_COLUMNS
204 };
205
206 /* Other */
207 #define GUIMSG_LINE_MAXLEN 256
208 static char pgui_errmsg[GUIMSG_LINE_MAXLEN+1];
209 static PGconn *pg_connection = NULL;
210 static SHPCONNECTIONCONFIG *conn = NULL;
211 static SHPLOADERCONFIG *global_loader_config = NULL;
212 static SHPDUMPERCONFIG *global_dumper_config = NULL;
213
214 static volatile int is_running = FALSE;
215
216 /* Local prototypes */
217 static void pgui_sanitize_connection_string(char *connection_string);
218
219
220 /*
221 ** Write a message to the Import Log text area.
222 */
223 void
pgui_log_va(const char * fmt,va_list ap)224 pgui_log_va(const char *fmt, va_list ap)
225 {
226 char msg[GUIMSG_LINE_MAXLEN+1];
227 GtkTextIter iter;
228
229 if ( -1 == vsnprintf (msg, GUIMSG_LINE_MAXLEN, fmt, ap) ) return;
230 msg[GUIMSG_LINE_MAXLEN] = '\0';
231
232 /* Append text to the end of the text area, scrolling if required to make it visible */
233 gtk_text_buffer_get_end_iter(textbuffer_log, &iter);
234 gtk_text_buffer_insert(textbuffer_log, &iter, msg, -1);
235 gtk_text_buffer_insert(textbuffer_log, &iter, "\n", -1);
236
237 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(textview_log), &iter, 0.0, TRUE, 0.0, 1.0);
238
239 /* Allow GTK to process events */
240 while (gtk_events_pending())
241 gtk_main_iteration();
242 }
243
244 /*
245 ** Write a message to the Import Log text area.
246 */
247 static void
pgui_logf(const char * fmt,...)248 pgui_logf(const char *fmt, ...)
249 {
250 va_list ap;
251 va_start(ap, fmt);
252
253 pgui_log_va(fmt, ap);
254
255 va_end(ap);
256 return;
257 }
258
259 /* Write an error message */
260 void
pgui_seterr_va(const char * fmt,va_list ap)261 pgui_seterr_va(const char *fmt, va_list ap)
262 {
263 if ( -1 == vsnprintf (pgui_errmsg, GUIMSG_LINE_MAXLEN, fmt, ap) ) return;
264 pgui_errmsg[GUIMSG_LINE_MAXLEN] = '\0';
265 }
266
267 static void
pgui_seterr(const char * fmt,...)268 pgui_seterr(const char *fmt, ...)
269 {
270 va_list ap;
271 va_start(ap, fmt);
272
273 pgui_seterr_va(fmt, ap);
274
275 va_end(ap);
276 return;
277 }
278
279 static void
pgui_raise_error_dialogue(void)280 pgui_raise_error_dialogue(void)
281 {
282 GtkWidget *dialog, *label;
283
284 label = gtk_label_new(pgui_errmsg);
285 dialog = gtk_dialog_new_with_buttons(_("Error"), GTK_WINDOW(window_main),
286 GTK_DIALOG_MODAL & GTK_DIALOG_NO_SEPARATOR & GTK_DIALOG_DESTROY_WITH_PARENT,
287 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
288 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE );
289 gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
290 gtk_container_set_border_width(GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), 15);
291 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
292 gtk_widget_show_all(dialog);
293 gtk_dialog_run(GTK_DIALOG(dialog));
294 gtk_widget_destroy(dialog);
295 return;
296 }
297
298 /*
299 ** Run a SQL command against the current connection.
300 */
301 static int
pgui_exec(const char * sql)302 pgui_exec(const char *sql)
303 {
304 PGresult *res = NULL;
305 ExecStatusType status;
306 char sql_trunc[256];
307
308 /* We need a connection to do anything. */
309 if ( ! pg_connection ) return 0;
310 if ( ! sql ) return 0;
311
312 res = PQexec(pg_connection, sql);
313 status = PQresultStatus(res);
314 PQclear(res);
315
316 /* Did something unexpected happen? */
317 if ( ! ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK ) )
318 {
319 /* Log notices and return success. */
320 if ( status == PGRES_NONFATAL_ERROR )
321 {
322 pgui_logf("%s", PQerrorMessage(pg_connection));
323 return 1;
324 }
325
326 /* Log errors and return failure. */
327 snprintf(sql_trunc, 255, "%s", sql);
328 pgui_logf("Failed SQL begins: \"%s\"", sql_trunc);
329 pgui_logf("Failed in pgui_exec(): %s", PQerrorMessage(pg_connection));
330 return 0;
331 }
332
333 return 1;
334 }
335
336 /*
337 ** Start the COPY process.
338 */
339 static int
pgui_copy_start(const char * sql)340 pgui_copy_start(const char *sql)
341 {
342 PGresult *res = NULL;
343 ExecStatusType status;
344 char sql_trunc[256];
345
346 /* We need a connection to do anything. */
347 if ( ! pg_connection ) return 0;
348 if ( ! sql ) return 0;
349
350 res = PQexec(pg_connection, sql);
351 status = PQresultStatus(res);
352 PQclear(res);
353
354 /* Did something unexpected happen? */
355 if ( status != PGRES_COPY_IN )
356 {
357 /* Log errors and return failure. */
358 snprintf(sql_trunc, 255, "%s", sql);
359 pgui_logf("Failed SQL begins: \"%s\"", sql_trunc);
360 pgui_logf("Failed in pgui_copy_start(): %s", PQerrorMessage(pg_connection));
361 return 0;
362 }
363
364 return 1;
365 }
366
367 /*
368 ** Send a line (row) of data into the COPY procedure.
369 */
370 static int
pgui_copy_write(const char * line)371 pgui_copy_write(const char *line)
372 {
373 char line_trunc[256];
374
375 /* We need a connection to do anything. */
376 if ( ! pg_connection ) return 0;
377 if ( ! line ) return 0;
378
379 /* Did something unexpected happen? */
380 if ( PQputCopyData(pg_connection, line, strlen(line)) < 0 )
381 {
382 /* Log errors and return failure. */
383 snprintf(line_trunc, 255, "%s", line);
384 pgui_logf("Failed row begins: \"%s\"", line_trunc);
385 pgui_logf("Failed in pgui_copy_write(): %s", PQerrorMessage(pg_connection));
386 return 0;
387 }
388
389 /* Send linefeed to signify end of line */
390 PQputCopyData(pg_connection, "\n", 1);
391
392 return 1;
393 }
394
395 /*
396 ** Finish the COPY process.
397 */
398 static int
pgui_copy_end(const int rollback)399 pgui_copy_end(const int rollback)
400 {
401 char *errmsg = NULL;
402
403 /* We need a connection to do anything. */
404 if ( ! pg_connection ) return 0;
405
406 if ( rollback ) errmsg = "Roll back the copy.";
407
408 /* Did something unexpected happen? */
409 if ( PQputCopyEnd(pg_connection, errmsg) < 0 )
410 {
411 /* Log errors and return failure. */
412 pgui_logf("Failed in pgui_copy_end(): %s", PQerrorMessage(pg_connection));
413 return 0;
414 }
415
416 return 1;
417 }
418
419 /*
420 * Ensures that the filename field width is within the stated bounds, and
421 * 'appropriately' sized, for some definition of 'appropriately'.
422 */
423 static void
update_filename_field_width(void)424 update_filename_field_width(void)
425 {
426 GtkTreeIter iter;
427 gboolean is_valid;
428 gchar *filename;
429 int max_width;
430
431 /* Loop through the list store to find the maximum length of an entry */
432 max_width = 0;
433 is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
434 while (is_valid)
435 {
436 /* Grab the length of the filename entry in characters */
437 gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_FILENAME_COLUMN, &filename, -1);
438 if (strlen(filename) > max_width)
439 max_width = strlen(filename);
440
441 /* Get next entry */
442 is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
443 }
444
445 /* Note the layout manager will handle the minimum size for us; we just need to be concerned with
446 making sure we don't exceed a maximum limit */
447 if (max_width > SHAPEFIELDMAXWIDTH)
448 g_object_set(import_filename_renderer, "width-chars", SHAPEFIELDMAXWIDTH, NULL);
449 else
450 g_object_set(import_filename_renderer, "width-chars", -1, NULL);
451
452 return;
453 }
454
455 /*
456 * This will create a connection to the database, just to see if it can.
457 * It cleans up after itself like a good little function and maintains
458 * the status of the valid_connection parameter.
459 */
460 static int
connection_test(void)461 connection_test(void)
462 {
463 char *connection_string = NULL;
464 char *connection_sanitized = NULL;
465
466 if (!(connection_string = ShpDumperGetConnectionStringFromConn(conn)))
467 {
468 pgui_raise_error_dialogue();
469 valid_connection = 0;
470 return 0;
471 }
472
473 connection_sanitized = strdup(connection_string);
474 pgui_sanitize_connection_string(connection_sanitized);
475 pgui_logf("Connecting: %s", connection_sanitized);
476 free(connection_sanitized);
477
478 pg_connection = PQconnectdb(connection_string);
479 if (PQstatus(pg_connection) == CONNECTION_BAD)
480 {
481 pgui_logf( _("Database connection failed: %s"), PQerrorMessage(pg_connection));
482 free(connection_string);
483 PQfinish(pg_connection);
484 pg_connection = NULL;
485 valid_connection = 0;
486 return 0;
487 }
488 PQfinish(pg_connection);
489 pg_connection = NULL;
490 free(connection_string);
491
492 valid_connection = 1;
493 return 1;
494 }
495
496
497 /* === Generic window functions === */
498
499 /* Delete event handler for popups that simply returns TRUE to prevent GTK from
500 destroying the window and then hides it manually */
501 static gint
pgui_event_popup_delete(GtkWidget * widget,GdkEvent * event,gpointer data)502 pgui_event_popup_delete(GtkWidget *widget, GdkEvent *event, gpointer data)
503 {
504 gtk_widget_hide(GTK_WIDGET(widget));
505 return TRUE;
506 }
507
508 /* === Progress window functions === */
509
510 static void
pgui_action_progress_cancel(GtkDialog * dialog,gint response_id,gpointer user_data)511 pgui_action_progress_cancel(GtkDialog *dialog, gint response_id, gpointer user_data)
512 {
513 /* Stop the current import */
514 is_running = FALSE;
515
516 return;
517 }
518
519 static gint
pgui_action_progress_delete(GtkWidget * widget,GdkEvent * event,gpointer data)520 pgui_action_progress_delete(GtkWidget *widget, GdkEvent *event, gpointer data)
521 {
522 /* Stop the current import */
523 is_running = FALSE;
524
525 return TRUE;
526 }
527
528
529 /* === Loader option Window functions === */
530
531 /* Update the specified SHPLOADERCONFIG with the global settings from the Options dialog */
532 static void
update_loader_config_globals_from_options_ui(SHPLOADERCONFIG * config)533 update_loader_config_globals_from_options_ui(SHPLOADERCONFIG *config)
534 {
535 const char *entry_encoding = gtk_entry_get_text(GTK_ENTRY(entry_options_encoding));
536 gboolean preservecase = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_preservecase));
537 gboolean forceint = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_forceint));
538 gboolean createindex = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_autoindex));
539 gboolean dbfonly = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_dbfonly));
540 gboolean dumpformat = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_dumpformat));
541 gboolean geography = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_geography));
542 gboolean simplegeoms = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_simplegeoms));
543
544 if (geography)
545 {
546 config->geography = 1;
547
548 if (config->geo_col)
549 free(config->geo_col);
550
551 config->geo_col = strdup(GEOGRAPHY_DEFAULT);
552 }
553 else
554 {
555 config->geography = 0;
556
557 if (config->geo_col)
558 free(config->geo_col);
559
560 config->geo_col = strdup(GEOMETRY_DEFAULT);
561 }
562
563 /* Encoding */
564 if (entry_encoding && strlen(entry_encoding) > 0)
565 {
566 if (config->encoding)
567 free(config->encoding);
568
569 config->encoding = strdup(entry_encoding);
570 }
571
572 /* Preserve case */
573 if (preservecase)
574 config->quoteidentifiers = 1;
575 else
576 config->quoteidentifiers = 0;
577
578 /* No long integers in table */
579 if (forceint)
580 config->forceint4 = 1;
581 else
582 config->forceint4 = 0;
583
584 /* Create spatial index after load */
585 if (createindex)
586 config->createindex = 1;
587 else
588 config->createindex = 0;
589
590 /* Read the .shp file, don't ignore it */
591 if (dbfonly)
592 {
593 config->readshape = 0;
594
595 /* There will be no spatial column so don't create a spatial index */
596 config->createindex = 0;
597 }
598 else
599 config->readshape = 1;
600
601 /* Use COPY rather than INSERT format */
602 if (dumpformat)
603 config->dump_format = 1;
604 else
605 config->dump_format = 0;
606
607 /* Simple geometries only */
608 if (simplegeoms)
609 config->simple_geometries = 1;
610 else
611 config->simple_geometries = 0;
612
613 return;
614 }
615
616 /* Update the loader options dialog with the current values from the global config */
617 static void
update_options_ui_from_loader_config_globals(void)618 update_options_ui_from_loader_config_globals(void)
619 {
620 gtk_entry_set_text(GTK_ENTRY(entry_options_encoding), global_loader_config->encoding);
621 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_preservecase), global_loader_config->quoteidentifiers ? TRUE : FALSE);
622 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_forceint), global_loader_config->forceint4 ? TRUE : FALSE);
623 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_autoindex), global_loader_config->createindex ? TRUE : FALSE);
624 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_dbfonly), global_loader_config->readshape ? FALSE : TRUE);
625 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_dumpformat), global_loader_config->dump_format ? TRUE : FALSE);
626 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_geography), global_loader_config->geography ? TRUE : FALSE);
627 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_simplegeoms), global_loader_config->simple_geometries ? TRUE : FALSE);
628
629 return;
630 }
631
632 /* Set the global config variables controlled by the options dialogue */
633 static void
pgui_set_loader_configs_from_options_ui()634 pgui_set_loader_configs_from_options_ui()
635 {
636 GtkTreeIter iter;
637 gboolean is_valid;
638 gpointer gptr;
639 SHPLOADERCONFIG *loader_file_config;
640
641 /* First update the global (template) configuration */
642 update_loader_config_globals_from_options_ui(global_loader_config);
643
644 /* Now also update the same settings for any existing files already added. We
645 do this by looping through all entries and updating their config too. */
646 is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
647 while (is_valid)
648 {
649 /* Get the SHPLOADERCONFIG for this file entry */
650 gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
651 loader_file_config = (SHPLOADERCONFIG *)gptr;
652
653 /* Update it */
654 update_loader_config_globals_from_options_ui(loader_file_config);
655
656 /* Get next entry */
657 is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
658 }
659
660 return;
661 }
662
663
664 /* === Table selection dialog functions === */
665
666 /* Load the model with information from the database */
667 static void
update_table_chooser_from_database()668 update_table_chooser_from_database()
669 {
670 PGresult *result, *geocol_result;
671 GtkTreeIter iter, geocol_iter;
672 GtkListStore *dumper_geocol_combo_list;
673 char *connection_string, *sql_form, *query, *schema, *table, *geocol_query, *geocol_name=NULL;
674 int hasgeo, i, j;
675
676 /* Open a connection to the database */
677 connection_string = ShpDumperGetConnectionStringFromConn(conn);
678 pg_connection = PQconnectdb(connection_string);
679
680 /* Here we find a list of all tables/views that not in a pg_* schema (or information_schema) and
681 we return the schema name, table name and whether or not the table/view contains any geo
682 columns */
683 query = "SELECT tableoids.oid, n.nspname, tableoids.relname, COALESCE((SELECT 1 from pg_attribute WHERE attrelid = tableoids.oid AND atttypid IN (SELECT oid FROM pg_type WHERE typname in ('geometry', 'geography')) LIMIT 1), 0) hasgeo FROM (SELECT c.oid, c.relname, c.relnamespace FROM pg_class c WHERE c.relkind IN ('r', 'v', 'm', 'f','p') AND c.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname NOT ILIKE 'pg_%' AND nspname <> 'information_schema')) tableoids, pg_namespace n WHERE tableoids.relnamespace = n.oid ORDER BY n.nspname, tableoids.relname";
684
685 result = PQexec(pg_connection, query);
686
687 /* Free any existing entries in the model */
688 gtk_list_store_clear(chooser_table_list_store);
689
690 /* Now insert one row for each query result */
691 for (i = 0; i < PQntuples(result); i++)
692 {
693 gtk_list_store_insert_before(chooser_table_list_store, &iter, NULL);
694
695 /* Look up the geo columns; if there are none then we set the field to (None). If we have just one
696 column then we set the column name directly. If we have more than one then we create a combo
697 dropdown containing the column names */
698 schema = PQgetvalue(result, i, PQfnumber(result, "nspname"));
699 table = PQgetvalue(result, i, PQfnumber(result, "relname"));
700
701 sql_form = "SELECT n.nspname, c.relname, a.attname FROM pg_class c, pg_namespace n, pg_attribute a WHERE c.relnamespace = n.oid AND n.nspname = '%s' AND c.relname = '%s' AND a.attrelid = c.oid AND a.atttypid IN (SELECT oid FROM pg_type WHERE typname in ('geometry', 'geography'))";
702
703 geocol_query = malloc(strlen(sql_form) + strlen(schema) + strlen(table) + 1);
704 sprintf(geocol_query, sql_form, schema, table);
705
706 geocol_result = PQexec(pg_connection, geocol_query);
707
708 /* Create a combo list loaded with the column names. Note that while we create the model and load
709 the data here, we don't actually display the geo column in this dialog. Instead we build the
710 list here so that we can pass to the export table list store when creating a new entry. This
711 is to ensure that the export table list model can directly represent a SHPDUMPERCONFIG. */
712 dumper_geocol_combo_list = gtk_list_store_new(TABLECHOOSER_GEOCOL_COMBO_COLUMNS, G_TYPE_STRING);
713
714 if (PQntuples(geocol_result) > 0)
715 {
716 /* Load the columns into the list store */
717 for (j = 0; j < PQntuples(geocol_result); j++)
718 {
719 geocol_name = PQgetvalue(geocol_result, j, PQfnumber(geocol_result, "attname"));
720
721 gtk_list_store_insert_before(dumper_geocol_combo_list, &geocol_iter, (GtkTreeIter *)TABLECHOOSER_GEOCOL_COMBO_TEXT);
722 gtk_list_store_set(dumper_geocol_combo_list, &geocol_iter,
723 TABLECHOOSER_GEOCOL_COMBO_TEXT, geocol_name,
724 -1);
725 }
726 }
727 else
728 {
729 /* Add a "default" entry */
730 geocol_name = NULL;
731
732 gtk_list_store_insert_before(dumper_geocol_combo_list, &geocol_iter, (GtkTreeIter *)TABLECHOOSER_GEOCOL_COMBO_TEXT);
733 gtk_list_store_set(dumper_geocol_combo_list, &geocol_iter,
734 TABLECHOOSER_GEOCOL_COMBO_TEXT, _("(None)"),
735 -1);
736 }
737
738 /* Free the query result */
739 PQclear(geocol_result);
740
741 /* Free the query string */
742 free(geocol_query);
743
744 /* Set the list store data */
745 hasgeo = atoi(PQgetvalue(result, i, PQfnumber(result, "hasgeo")));
746 gtk_list_store_set(chooser_table_list_store, &iter,
747 TABLECHOOSER_SCHEMA_COLUMN, schema,
748 TABLECHOOSER_TABLE_COLUMN, table,
749 TABLECHOOSER_GEO_LISTSTORE_COLUMN, dumper_geocol_combo_list,
750 TABLECHOOSER_GEO_COLUMN, geocol_name,
751 TABLECHOOSER_HASGEO_COLUMN, hasgeo,
752 -1);
753 }
754
755 /* Clear up the result set */
756 PQclear(result);
757
758 /* Close the existing connection */
759 PQfinish(pg_connection);
760 pg_connection = NULL;
761
762 return;
763 }
764
765 /* GtkTreeModelFilter visibility function */
766 static gboolean
table_chooser_visibility_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)767 table_chooser_visibility_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
768 {
769 /* First determine whether the hasgeo tickbox is selected or not */
770 gboolean geoonly = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_chooser_geoonly));
771 int hasgeo;
772
773 /* If unticked then we show all tables */
774 if (!geoonly)
775 return TRUE;
776 else
777 {
778 /* Otherwise we only show the tables with geo columns */
779 gtk_tree_model_get(GTK_TREE_MODEL(model), iter, TABLECHOOSER_HASGEO_COLUMN, &hasgeo, -1);
780 if (hasgeo)
781 return TRUE;
782 else
783 return FALSE;
784 }
785
786 return FALSE;
787 }
788
789 /* === Dumper option Window functions === */
790
791 /* Update the specified SHPDUMPERCONFIG with the global settings from the Options dialog */
792 static void
update_dumper_config_globals_from_options_ui(SHPDUMPERCONFIG * config)793 update_dumper_config_globals_from_options_ui(SHPDUMPERCONFIG *config)
794 {
795 gboolean includegid = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_includegid));
796 gboolean keep_fieldname_case = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_keep_fieldname_case));
797 gboolean unescapedattrs = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_unescapedattrs));
798
799 /* Include gid or not */
800 if (includegid)
801 config->includegid = 1;
802 else
803 config->includegid = 0;
804
805 /* Keep fieldname case */
806 if (keep_fieldname_case)
807 config->keep_fieldname_case = 1;
808 else
809 config->keep_fieldname_case = 0;
810
811 /* Escape column names or not */
812 if (unescapedattrs)
813 config->unescapedattrs = 1;
814 else
815 config->unescapedattrs = 0;
816
817 return;
818 }
819
820 /* Update the options dialog with the current values from the global config */
821 static void
update_options_ui_from_dumper_config_globals(void)822 update_options_ui_from_dumper_config_globals(void)
823 {
824 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_includegid), global_dumper_config->includegid ? TRUE : FALSE);
825 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_keep_fieldname_case), global_dumper_config->keep_fieldname_case ? TRUE : FALSE);
826 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_unescapedattrs), global_dumper_config->unescapedattrs ? TRUE : FALSE);
827
828 return;
829 }
830
831 /* Set the global config variables controlled by the options dialogue */
832 static void
pgui_set_dumper_configs_from_options_ui()833 pgui_set_dumper_configs_from_options_ui()
834 {
835 GtkTreeIter iter;
836 gboolean is_valid;
837 gpointer gptr;
838 SHPDUMPERCONFIG *dumper_table_config;
839
840 /* First update the global (template) configuration */
841 update_dumper_config_globals_from_options_ui(global_dumper_config);
842
843 /* Now also update the same settings for any existing tables already added. We
844 do this by looping through all entries and updating their config too. */
845 is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(export_table_list_store), &iter);
846 while (is_valid)
847 {
848 /* Get the SHPDUMPERCONFIG for this file entry */
849 gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
850 dumper_table_config = (SHPDUMPERCONFIG *)gptr;
851
852 /* Update it */
853 update_dumper_config_globals_from_options_ui(dumper_table_config);
854
855 /* Get next entry */
856 is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(export_table_list_store), &iter);
857 }
858
859 return;
860 }
861
862 /* Signal handler for ticking/unticking the "only show geo columns" box */
863 static void
pgui_action_chooser_toggle_show_geocolumn(GtkToggleButton * togglebutton,gpointer user_data)864 pgui_action_chooser_toggle_show_geocolumn(GtkToggleButton *togglebutton, gpointer user_data)
865 {
866 /* Simply update the listview filter */
867 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(chooser_filtered_table_list_store));
868
869 return;
870 }
871
872 static void
pgui_action_dumper_options_open(GtkWidget * widget,gpointer data)873 pgui_action_dumper_options_open(GtkWidget *widget, gpointer data)
874 {
875 update_options_ui_from_dumper_config_globals();
876 gtk_widget_show_all(dialog_dumper_options);
877 return;
878 }
879
880 static void
pgui_action_dumper_options_close(GtkWidget * widget,gint response,gpointer data)881 pgui_action_dumper_options_close(GtkWidget *widget, gint response, gpointer data)
882 {
883 /* Only update the configuration if the user hit OK */
884 if (response == GTK_RESPONSE_OK)
885 pgui_set_dumper_configs_from_options_ui();
886
887 /* Hide the dialog */
888 gtk_widget_hide(dialog_dumper_options);
889
890 return;
891 }
892
893 /* === Main window functions === */
894
895 /* Given a filename, generate a new loader configuration */
896 static SHPLOADERCONFIG *
create_new_file_config(const char * filename)897 create_new_file_config(const char *filename)
898 {
899 SHPLOADERCONFIG *loader_file_config;
900 char *table_start, *table_end;
901 int i;
902
903 /* Generate a new configuration by copying the global options first and then
904 adding in the specific values for this file */
905 loader_file_config = malloc(sizeof(SHPLOADERCONFIG));
906 memcpy(loader_file_config, global_loader_config, sizeof(SHPLOADERCONFIG));
907
908 /* Note: we must copy the encoding here since it is the only pass-by-reference
909 type set in set_loader_config_defaults() and each config needs its own copy
910 of any referenced items */
911 loader_file_config->encoding = strdup(global_loader_config->encoding);
912
913 /* Copy the filename (we'll remove the .shp extension in a sec) */
914 loader_file_config->shp_file = strdup(filename);
915
916 /* Generate the default table name from the filename */
917 table_start = loader_file_config->shp_file + strlen(loader_file_config->shp_file);
918 while (*table_start != '/' && *table_start != '\\' && table_start > loader_file_config->shp_file)
919 table_start--;
920
921 /* Forward one to start of actual characters */
922 table_start++;
923
924 /* Roll back from end to first . character. */
925 table_end = loader_file_config->shp_file + strlen(loader_file_config->shp_file);
926 while (*table_end != '.' && table_end > loader_file_config->shp_file && table_end > table_start )
927 table_end--;
928
929 /* Copy the table name */
930 loader_file_config->table = malloc(table_end - table_start + 1);
931 memcpy(loader_file_config->table, table_start, table_end - table_start);
932 loader_file_config->table[table_end - table_start] = '\0';
933
934 /* Force the table name to lower case */
935 for (i = 0; i < table_end - table_start; i++)
936 {
937 if (isupper(loader_file_config->table[i]) != 0)
938 loader_file_config->table[i] = tolower(loader_file_config->table[i]);
939 }
940
941 /* Set the default schema to public */
942 loader_file_config->schema = strdup("public");
943
944 /* Set the default geo column name */
945 if (global_loader_config->geography)
946 loader_file_config->geo_col = strdup(GEOGRAPHY_DEFAULT);
947 else
948 loader_file_config->geo_col = strdup(GEOMETRY_DEFAULT);
949
950 return loader_file_config;
951 }
952
953 /* Given the loader configuration, add a new row representing this file to the listview */
954 static void
add_loader_file_config_to_list(SHPLOADERCONFIG * loader_file_config)955 add_loader_file_config_to_list(SHPLOADERCONFIG *loader_file_config)
956 {
957 GtkTreeIter iter;
958 #define MAXLEN 16
959 char srid[MAXLEN+1];
960
961 /* Convert SRID into string */
962 if ( MAXLEN+1 <= snprintf(srid, MAXLEN+1, "%d", loader_file_config->sr_id) )
963 {
964 pgui_logf("Invalid SRID requiring more than %d digits: %d", MAXLEN, loader_file_config->sr_id);
965 pgui_raise_error_dialogue();
966 srid[MAXLEN] = '\0';
967 }
968
969 gtk_list_store_insert_before(import_file_list_store, &iter, NULL);
970 gtk_list_store_set(import_file_list_store, &iter,
971 IMPORT_POINTER_COLUMN, loader_file_config,
972 IMPORT_FILENAME_COLUMN, loader_file_config->shp_file,
973 IMPORT_SCHEMA_COLUMN, loader_file_config->schema,
974 IMPORT_TABLE_COLUMN, loader_file_config->table,
975 IMPORT_GEOMETRY_COLUMN, loader_file_config->geo_col,
976 IMPORT_SRID_COLUMN, srid,
977 IMPORT_MODE_COLUMN, _("Create"),
978 -1);
979
980 /* Update the filename field width */
981 update_filename_field_width();
982
983 return;
984 }
985
986 /* Free up the specified SHPLOADERCONFIG */
987 static void
free_loader_config(SHPLOADERCONFIG * config)988 free_loader_config(SHPLOADERCONFIG *config)
989 {
990
991 if (config->table)
992 free(config->table);
993
994 if (config->schema)
995 free(config->schema);
996
997 if (config->geo_col)
998 free(config->geo_col);
999
1000 if (config->shp_file)
1001 free(config->shp_file);
1002
1003 if (config->encoding)
1004 free(config->encoding);
1005
1006 if (config->tablespace)
1007 free(config->tablespace);
1008
1009 if (config->idxtablespace)
1010 free(config->idxtablespace);
1011
1012 /* Free the config itself */
1013 free(config);
1014 }
1015
1016 /* Given a table selection, generate a new configuration */
1017 static SHPDUMPERCONFIG *
create_new_table_config(GtkTreeIter * iter)1018 create_new_table_config(GtkTreeIter *iter)
1019 {
1020 SHPDUMPERCONFIG *dumper_table_config;
1021 gchar *schema, *table, *geocol;
1022 gint hasgeo;
1023
1024 /* Generate a new configuration by copying the global options first and then
1025 adding in the specific values for this table */
1026 dumper_table_config = malloc(sizeof(SHPDUMPERCONFIG));
1027 memcpy(dumper_table_config, global_dumper_config, sizeof(SHPDUMPERCONFIG));
1028
1029 /* Grab the values from the current iter */
1030 gtk_tree_model_get(GTK_TREE_MODEL(chooser_filtered_table_list_store), iter,
1031 TABLECHOOSER_SCHEMA_COLUMN, &schema,
1032 TABLECHOOSER_TABLE_COLUMN, &table,
1033 TABLECHOOSER_GEO_COLUMN, &geocol,
1034 TABLECHOOSER_HASGEO_COLUMN, &hasgeo,
1035 -1);
1036
1037 /* Set up the values in the SHPDUMPERCONFIG */
1038 dumper_table_config->schema = strdup(schema);
1039 dumper_table_config->table = strdup(table);
1040
1041 /* We also set the filename the same as the table name */
1042 dumper_table_config->shp_file = strdup(table);
1043
1044 if (hasgeo && geocol)
1045 dumper_table_config->geo_col_name = strdup(geocol);
1046 else
1047 dumper_table_config->geo_col_name = NULL;
1048
1049 return dumper_table_config;
1050 }
1051
1052 /* Given the dumper configuration, add a new row representing this file to the listview. The liststore and iter arguments
1053 are optional, and enable the user to specify additional information to the view, e.g. geo column multi-choice. */
1054 static void
add_dumper_table_config_to_list(SHPDUMPERCONFIG * dumper_table_config,GtkListStore * chooser_liststore,GtkTreeIter * chooser_iter)1055 add_dumper_table_config_to_list(SHPDUMPERCONFIG *dumper_table_config, GtkListStore *chooser_liststore, GtkTreeIter *chooser_iter)
1056 {
1057 GtkTreeIter iter;
1058 GtkListStore *geocol_liststore;
1059
1060 gtk_list_store_insert_before(export_table_list_store, &iter, NULL);
1061 gtk_list_store_set(export_table_list_store, &iter,
1062 EXPORT_POINTER_COLUMN, dumper_table_config,
1063 EXPORT_SCHEMA_COLUMN, dumper_table_config->schema,
1064 EXPORT_TABLE_COLUMN, dumper_table_config->table,
1065 EXPORT_GEOMETRY_COLUMN, dumper_table_config->geo_col_name,
1066 EXPORT_FILENAME_COLUMN, dumper_table_config->shp_file,
1067 -1);
1068
1069 /* If we have supplied the table_chooser store for additional information, use it */
1070 if (chooser_liststore)
1071 {
1072 /* Let's add a multi-choice geometry column to the table */
1073 gtk_tree_model_get(GTK_TREE_MODEL(chooser_liststore), chooser_iter,
1074 TABLECHOOSER_GEO_LISTSTORE_COLUMN, &geocol_liststore,
1075 -1);
1076
1077 gtk_list_store_set(export_table_list_store, &iter,
1078 EXPORT_GEOMETRY_LISTSTORE_COLUMN, geocol_liststore,
1079 -1);
1080 }
1081
1082 return;
1083 }
1084
1085 /* Free up the specified SHPDUMPERCONFIG */
1086 static void
free_dumper_config(SHPDUMPERCONFIG * config)1087 free_dumper_config(SHPDUMPERCONFIG *config)
1088 {
1089
1090 if (config->table)
1091 free(config->table);
1092
1093 if (config->schema)
1094 free(config->schema);
1095
1096 if (config->geo_col_name)
1097 free(config->geo_col_name);
1098
1099 if (config->shp_file)
1100 free(config->shp_file);
1101
1102 /* Free the config itself */
1103 free(config);
1104 }
1105
1106 /* Validate a single DBF column type against a PostgreSQL type: return either TRUE or FALSE depending
1107 upon whether or not the type is (broadly) compatible */
1108 static int
validate_shape_column_against_pg_column(int dbf_fieldtype,char * pg_fieldtype)1109 validate_shape_column_against_pg_column(int dbf_fieldtype, char *pg_fieldtype)
1110 {
1111 switch (dbf_fieldtype)
1112 {
1113 case FTString:
1114 /* Only varchar */
1115 if (!strcmp(pg_fieldtype, "varchar"))
1116 return -1;
1117 break;
1118
1119 case FTDate:
1120 /* Only date */
1121 if (!strcmp(pg_fieldtype, "date"))
1122 return -1;
1123 break;
1124
1125 case FTInteger:
1126 /* Tentatively allow int2, int4 and numeric */
1127 if (!strcmp(pg_fieldtype, "int2") || !strcmp(pg_fieldtype, "int4") || !strcmp(pg_fieldtype, "numeric"))
1128 return -1;
1129 break;
1130
1131 case FTDouble:
1132 /* Only float8/numeric */
1133 if (!strcmp(pg_fieldtype, "float8") || !strcmp(pg_fieldtype, "numeric"))
1134 return -1;
1135 break;
1136
1137 case FTLogical:
1138 /* Only boolean */
1139 if (!strcmp(pg_fieldtype, "boolean"))
1140 return -1;
1141 break;
1142 }
1143
1144 /* Otherwise we can't guarantee this (but this is just a warning anyway) */
1145 return 0;
1146 }
1147
1148 /* Validate column compatibility for the given loader configuration against the table/column
1149 list returned in result */
1150 static int
validate_remote_loader_columns(SHPLOADERCONFIG * config,PGresult * result)1151 validate_remote_loader_columns(SHPLOADERCONFIG *config, PGresult *result)
1152 {
1153 ExecStatusType status;
1154 SHPLOADERSTATE *state;
1155 int ntuples;
1156 char *pg_fieldname, *pg_fieldtype;
1157 int ret, i, j, found, response = SHPLOADEROK;
1158
1159 /* Check the status of the result set */
1160 status = PQresultStatus(result);
1161 if (status == PGRES_TUPLES_OK)
1162 {
1163 ntuples = PQntuples(result);
1164
1165 switch (config->opt)
1166 {
1167 case 'c':
1168 /* If we have a row matching the table given in the config, then it already exists */
1169 if (ntuples > 0)
1170 {
1171 pgui_seterr(_("ERROR: Create mode selected for existing table: %s.%s"), config->schema, config->table);
1172 response = SHPLOADERERR;
1173 }
1174 break;
1175
1176 case 'p':
1177 /* If we have a row matching the table given in the config, then it already exists */
1178 if (ntuples > 0)
1179 {
1180 pgui_seterr(_("ERROR: Prepare mode selected for existing table: %s.%s"), config->schema, config->table);
1181 response = SHPLOADERERR;
1182 }
1183 break;
1184
1185 case 'a':
1186 /* If we are trying to append to a table but it doesn't exist, emit a warning */
1187 if (ntuples == 0)
1188 {
1189 pgui_seterr(_("ERROR: Destination table %s.%s could not be found for appending"), config->schema, config->table);
1190 response = SHPLOADERERR;
1191 }
1192 else
1193 {
1194 /* If we have a row then lets do some simple column validation... */
1195 state = ShpLoaderCreate(config);
1196 ret = ShpLoaderOpenShape(state);
1197 if (ret != SHPLOADEROK)
1198 {
1199 pgui_logf(_("Warning: Could not load shapefile %s"), config->shp_file);
1200 ShpLoaderDestroy(state);
1201 }
1202
1203 /* Find each column based upon its name and then validate type separately... */
1204 for (i = 0; i < state->num_fields; i++)
1205 {
1206 /* Make sure we find a column */
1207 found = 0;
1208 for (j = 0; j < ntuples; j++)
1209 {
1210 pg_fieldname = PQgetvalue(result, j, PQfnumber(result, "field"));
1211 pg_fieldtype = PQgetvalue(result, j, PQfnumber(result, "type"));
1212
1213 if (!strcmp(state->field_names[i], pg_fieldname))
1214 {
1215 found = -1;
1216
1217 ret = validate_shape_column_against_pg_column(state->types[i], pg_fieldtype);
1218 if (!ret)
1219 {
1220 pgui_logf(_("Warning: DBF Field '%s' is not compatible with PostgreSQL column '%s' in %s.%s"), state->field_names[i], pg_fieldname, config->schema, config->table);
1221 response = SHPLOADERWARN;
1222 }
1223 }
1224 }
1225
1226 /* Flag a warning if we can't find a match */
1227 if (!found)
1228 {
1229 pgui_logf(_("Warning: DBF Field '%s' within file %s could not be matched to a column within table %s.%s"),
1230 state->field_names[i], config->shp_file, config->schema, config->table);
1231 response = SHPLOADERWARN;
1232 }
1233 }
1234
1235 ShpLoaderDestroy(state);
1236 }
1237
1238 break;
1239 }
1240 }
1241 else
1242 {
1243 pgui_seterr(_("ERROR: unable to process validation response from remote server"));
1244 response = SHPLOADERERR;
1245 }
1246
1247 return response;
1248 }
1249
1250 /* Terminate the main loop and exit the application. */
1251 static void
pgui_quit(GtkWidget * widget,gpointer data)1252 pgui_quit (GtkWidget *widget, gpointer data)
1253 {
1254 gtk_main_quit();
1255 }
1256
1257 static void
pgui_action_about_open()1258 pgui_action_about_open()
1259 {
1260 /* Display the dialog and hide it again upon exit */
1261 gtk_dialog_run(GTK_DIALOG(dialog_about));
1262 gtk_widget_hide(dialog_about);
1263 }
1264
1265 static void
pgui_action_cancel(GtkWidget * widget,gpointer data)1266 pgui_action_cancel(GtkWidget *widget, gpointer data)
1267 {
1268 if (!is_running)
1269 pgui_quit(widget, data); /* quit if we're not running */
1270 else
1271 is_running = FALSE;
1272 }
1273
1274 static void
pgui_action_loader_options_open(GtkWidget * widget,gpointer data)1275 pgui_action_loader_options_open(GtkWidget *widget, gpointer data)
1276 {
1277 update_options_ui_from_loader_config_globals();
1278 gtk_widget_show_all(dialog_loader_options);
1279 return;
1280 }
1281
1282 static void
pgui_action_loader_options_close(GtkWidget * widget,gint response,gpointer data)1283 pgui_action_loader_options_close(GtkWidget *widget, gint response, gpointer data)
1284 {
1285 /* Only update the configuration if the user hit OK */
1286 if (response == GTK_RESPONSE_OK)
1287 pgui_set_loader_configs_from_options_ui();
1288
1289 /* Hide the dialog */
1290 gtk_widget_hide(dialog_loader_options);
1291
1292 return;
1293 }
1294
1295 static void
pgui_action_open_file_dialog(GtkWidget * widget,gpointer data)1296 pgui_action_open_file_dialog(GtkWidget *widget, gpointer data)
1297 {
1298 SHPLOADERCONFIG *loader_file_config;
1299 GSList *filename_list, *filename_item;
1300 gchar *filename;
1301
1302 /* Make sure we deselect any files from the last time */
1303 gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog_filechooser));
1304
1305 /* Run the dialog */
1306 if (gtk_dialog_run(GTK_DIALOG(dialog_filechooser)) == GTK_RESPONSE_ACCEPT)
1307 {
1308 /* Create the new file configuration based upon the each filename and add it to the listview */
1309 filename_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog_filechooser));
1310
1311 filename_item = g_slist_nth(filename_list, 0);
1312 while (filename_item)
1313 {
1314 /* Add the configuration */
1315 filename = g_slist_nth_data(filename_item, 0);
1316
1317 loader_file_config = create_new_file_config(filename);
1318 add_loader_file_config_to_list(loader_file_config);
1319
1320 /* Grab the next filename */
1321 filename_item = g_slist_next(filename_item);
1322 }
1323
1324 /* Free the list */
1325 g_slist_free(filename_list);
1326 }
1327
1328 gtk_widget_hide(dialog_filechooser);
1329 }
1330
1331 static void
pgui_action_open_table_dialog(GtkWidget * widget,gpointer data)1332 pgui_action_open_table_dialog(GtkWidget *widget, gpointer data)
1333 {
1334 SHPDUMPERCONFIG *dumper_table_config;
1335 GtkTreeSelection *chooser_selection;
1336 GtkTreeModel *model;
1337 GList *selected_rows_list, *selected_row;
1338 GtkTreeIter iter;
1339 GtkTreePath *tree_path;
1340
1341 /* Make sure we can connect to the database first */
1342 if (!connection_test())
1343 {
1344 pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
1345 pgui_raise_error_dialogue();
1346
1347 /* Open the connections UI for the user */
1348 update_conn_ui_from_conn_config();
1349
1350 gtk_widget_show_all(GTK_WIDGET(window_conn));
1351 return;
1352 }
1353
1354 /* Setup the form */
1355 update_table_chooser_from_database();
1356 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(chooser_filtered_table_list_store));
1357
1358 /* Run the dialog */
1359 gtk_widget_show_all(dialog_tablechooser);
1360 if (gtk_dialog_run(GTK_DIALOG(dialog_tablechooser)) == GTK_RESPONSE_OK)
1361 {
1362 /* Create the new dumper configuration based upon the selected iters and add them to the listview */
1363 chooser_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(chooser_tree));
1364
1365 selected_rows_list = gtk_tree_selection_get_selected_rows(chooser_selection, &model);
1366 selected_row = g_list_first(selected_rows_list);
1367 while (selected_row)
1368 {
1369 /* Get the tree iter */
1370 tree_path = (GtkTreePath *)g_list_nth_data(selected_row, 0);
1371 gtk_tree_model_get_iter(model, &iter, tree_path);
1372
1373 /* Get the config and add it to the list */
1374 dumper_table_config = create_new_table_config(&iter);
1375 add_dumper_table_config_to_list(dumper_table_config, chooser_filtered_table_list_store, &iter);
1376
1377 /* Get the next row */
1378 selected_row = g_list_next(selected_row);
1379 }
1380
1381 /* Free the GList */
1382 g_list_foreach(selected_row, (GFunc)gtk_tree_path_free, NULL);
1383 g_list_free(selected_row);
1384 }
1385
1386 gtk_widget_hide(dialog_tablechooser);
1387 }
1388
1389 /*
1390 * Signal handler for the remove box. Performs no user interaction, simply
1391 * removes the row from the table.
1392 */
1393 static void
pgui_action_handle_table_remove(GtkCellRendererToggle * renderer,gchar * path,gpointer user_data)1394 pgui_action_handle_table_remove(GtkCellRendererToggle *renderer,
1395 gchar *path,
1396 gpointer user_data)
1397 {
1398 GtkTreeIter iter;
1399 SHPDUMPERCONFIG *dumper_table_config;
1400 gpointer gptr;
1401
1402 /* Grab the SHPDUMPERCONFIG from the EXPORT_POINTER_COLUMN for the list store */
1403 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path);
1404 gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
1405 dumper_table_config = (SHPDUMPERCONFIG *)gptr;
1406
1407 /* Free the configuration from memory */
1408 free_dumper_config(dumper_table_config);
1409
1410 /* Remove the row from the list */
1411 gtk_list_store_remove(export_table_list_store, &iter);
1412 }
1413
1414 static void
pgui_action_import(GtkWidget * widget,gpointer data)1415 pgui_action_import(GtkWidget *widget, gpointer data)
1416 {
1417 SHPLOADERCONFIG *loader_file_config;
1418 SHPLOADERSTATE *state;
1419 gint is_valid;
1420 gpointer gptr;
1421 GtkTreeIter iter;
1422 char *sql_form, *query, *connection_string, *progress_shapefile = NULL;
1423 char progress_text[GUIMSG_LINE_MAXLEN+1];
1424 PGresult *result;
1425
1426 int ret, i = 0;
1427 char *header, *footer, *record;
1428
1429 /* Get the first row of the import list */
1430 is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
1431 if (!is_valid)
1432 {
1433 pgui_seterr(_("ERROR: You haven't specified any files to import"));
1434 pgui_raise_error_dialogue();
1435
1436 return;
1437 }
1438
1439 /* Firstly make sure that we can connect to the database - if we can't then there isn't much
1440 point doing anything else... */
1441 if (!connection_test())
1442 {
1443 pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
1444 pgui_raise_error_dialogue();
1445
1446 /* Open the connections UI for the user */
1447 update_conn_ui_from_conn_config();
1448
1449 gtk_widget_show_all(GTK_WIDGET(window_conn));
1450 return;
1451 }
1452
1453 /* Let's open a single connection to the remote DB for the duration of the validation pass;
1454 note that we already know the connection string works, otherwise we would have bailed
1455 out earlier in the function */
1456 connection_string = ShpDumperGetConnectionStringFromConn(conn);
1457 pg_connection = PQconnectdb(connection_string);
1458
1459 /* Setup the table/column type discovery query */
1460 sql_form = "SELECT a.attnum, a.attname AS field, t.typname AS type, a.attlen AS length, a.atttypmod AS precision FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n WHERE c.relname = '%s' AND n.nspname = '%s' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid AND c.relnamespace = n.oid ORDER BY a.attnum";
1461
1462 /* Validation: we loop through each of the files in order to validate them as a separate pass */
1463 while (is_valid)
1464 {
1465 /* Grab the SHPLOADERCONFIG for this row */
1466 gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
1467 loader_file_config = (SHPLOADERCONFIG *)gptr;
1468
1469 /* For each entry, we execute a remote query in order to determine the column names
1470 and types for the remote table if they actually exist */
1471 query = malloc(strlen(sql_form) + strlen(loader_file_config->schema) + strlen(loader_file_config->table) + 1);
1472 sprintf(query, sql_form, loader_file_config->table, loader_file_config->schema);
1473 result = PQexec(pg_connection, query);
1474
1475 /* Call the validation function with the SHPLOADERCONFIG and the result set */
1476 ret = validate_remote_loader_columns(loader_file_config, result);
1477 if (ret == SHPLOADERERR)
1478 {
1479 pgui_raise_error_dialogue();
1480
1481 PQclear(result);
1482 free(query);
1483
1484 return;
1485 }
1486
1487 /* Free the SQL query */
1488 PQclear(result);
1489 free(query);
1490
1491 /* Get next entry */
1492 is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
1493 }
1494
1495 /* Close our database connection */
1496 PQfinish(pg_connection);
1497
1498
1499 /* Once we've done the validation pass, now let's load the shapefile */
1500 is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
1501 while (is_valid)
1502 {
1503 /* Grab the SHPLOADERCONFIG for this row */
1504 gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
1505 loader_file_config = (SHPLOADERCONFIG *)gptr;
1506
1507 pgui_logf("\n==============================");
1508 pgui_logf("Importing with configuration: %s, %s, %s, %s, mode=%c, dump=%d, simple=%d, geography=%d, index=%d, shape=%d, srid=%d", loader_file_config->table, loader_file_config->schema, loader_file_config->geo_col, loader_file_config->shp_file, loader_file_config->opt, loader_file_config->dump_format, loader_file_config->simple_geometries, loader_file_config->geography, loader_file_config->createindex, loader_file_config->readshape, loader_file_config->sr_id);
1509
1510 /*
1511 * Loop through the items in the shapefile
1512 */
1513 is_running = TRUE;
1514
1515 /* One connection per file, otherwise error handling becomes tricky... */
1516 pg_connection = PQconnectdb(connection_string);
1517
1518 /* Disable the button to prevent multiple imports running at the same time */
1519 gtk_widget_set_sensitive(widget, FALSE);
1520
1521 /* Allow GTK events to get a look in */
1522 while (gtk_events_pending())
1523 gtk_main_iteration();
1524
1525 /* Create the shapefile state object */
1526 state = ShpLoaderCreate(loader_file_config);
1527
1528 /* Open the shapefile */
1529 ret = ShpLoaderOpenShape(state);
1530 if (ret != SHPLOADEROK)
1531 {
1532 pgui_logf("%s", state->message);
1533
1534 if (ret == SHPLOADERERR)
1535 goto import_cleanup;
1536 }
1537
1538 /* For progress display, only show the "core" filename */
1539 for (i = strlen(loader_file_config->shp_file); i >= 0
1540 && loader_file_config->shp_file[i - 1] != '\\' && loader_file_config->shp_file[i - 1] != '/'; i--);
1541
1542 progress_shapefile = malloc(strlen(loader_file_config->shp_file));
1543 strcpy(progress_shapefile, &loader_file_config->shp_file[i]);
1544
1545 /* Display the progress dialog */
1546 snprintf(progress_text, GUIMSG_LINE_MAXLEN, _("Importing shapefile %s (%d records)..."), progress_shapefile, ShpLoaderGetRecordCount(state));
1547 progress_text[GUIMSG_LINE_MAXLEN] = '\0';
1548 gtk_label_set_text(GTK_LABEL(label_progress), progress_text);
1549 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.0);
1550 gtk_widget_show_all(dialog_progress);
1551
1552 /* If reading the whole shapefile, display its type */
1553 if (state->config->readshape)
1554 {
1555 pgui_logf("Shapefile type: %s", SHPTypeName(state->shpfiletype));
1556 pgui_logf("PostGIS type: %s[%d]", state->pgtype, state->pgdims);
1557 }
1558
1559 /* Get the header */
1560 ret = ShpLoaderGetSQLHeader(state, &header);
1561 if (ret != SHPLOADEROK)
1562 {
1563 pgui_logf("%s", state->message);
1564
1565 if (ret == SHPLOADERERR)
1566 goto import_cleanup;
1567 }
1568
1569 /* Send the header to the remote server: if we are in COPY mode then the last
1570 statement will be a COPY and so will change connection mode */
1571 ret = pgui_exec(header);
1572 free(header);
1573
1574 if (!ret)
1575 goto import_cleanup;
1576
1577 /* If we are in prepare mode, we need to skip the actual load. */
1578 if (state->config->opt != 'p')
1579 {
1580 int numrecords = ShpLoaderGetRecordCount(state);
1581 int records_per_tick = (numrecords / 200) - 1;
1582
1583 if ( records_per_tick < 1 )
1584 records_per_tick = 1;
1585
1586 /* If we are in COPY (dump format) mode, output the COPY statement and enter COPY mode */
1587 if (state->config->dump_format)
1588 {
1589 ret = ShpLoaderGetSQLCopyStatement(state, &header);
1590
1591 if (ret != SHPLOADEROK)
1592 {
1593 pgui_logf("%s", state->message);
1594
1595 if (ret == SHPLOADERERR)
1596 goto import_cleanup;
1597 }
1598
1599 /* Send the result to the remote server: this should put us in COPY mode */
1600 ret = pgui_copy_start(header);
1601 free(header);
1602
1603 if (!ret)
1604 goto import_cleanup;
1605 }
1606
1607 /* Main loop: iterate through all of the records and send them to stdout */
1608 for (i = 0; i < numrecords && is_running; i++)
1609 {
1610 ret = ShpLoaderGenerateSQLRowStatement(state, i, &record);
1611
1612 switch (ret)
1613 {
1614 case SHPLOADEROK:
1615 /* Simply send the statement */
1616 if (state->config->dump_format)
1617 ret = pgui_copy_write(record);
1618 else
1619 ret = pgui_exec(record);
1620
1621 /* Display a record number if we failed */
1622 if (!ret)
1623 pgui_logf(_("Import failed on record number %d"), i);
1624
1625 free(record);
1626 break;
1627
1628 case SHPLOADERERR:
1629 /* Display the error message then stop */
1630 pgui_logf("%s\n", state->message);
1631 goto import_cleanup;
1632 break;
1633
1634 case SHPLOADERWARN:
1635 /* Display the warning, but continue */
1636 pgui_logf("%s\n", state->message);
1637
1638 if (state->config->dump_format)
1639 ret = pgui_copy_write(record);
1640 else
1641 ret = pgui_exec(record);
1642
1643 /* Display a record number if we failed */
1644 if (!ret)
1645 pgui_logf(_("Import failed on record number %d"), i);
1646
1647 free(record);
1648 break;
1649
1650 case SHPLOADERRECDELETED:
1651 /* Record is marked as deleted - ignore */
1652 break;
1653
1654 case SHPLOADERRECISNULL:
1655 /* Record is NULL and should be ignored according to NULL policy */
1656 break;
1657 }
1658
1659 /* Update the progress bar */
1660 if ( i % records_per_tick == 0 )
1661 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), (float)i / numrecords);
1662
1663 /* Allow GTK events to get a look in */
1664 while (gtk_events_pending())
1665 gtk_main_iteration();
1666 }
1667
1668 /* If we are in COPY (dump format) mode, leave COPY mode */
1669 if (state->config->dump_format)
1670 {
1671 if (! pgui_copy_end(0) )
1672 goto import_cleanup;
1673
1674 result = PQgetResult(pg_connection);
1675 if (PQresultStatus(result) != PGRES_COMMAND_OK)
1676 {
1677 pgui_logf(_("COPY failed with the following error: %s"), PQerrorMessage(pg_connection));
1678 ret = SHPLOADERERR;
1679 goto import_cleanup;
1680 }
1681 }
1682 } /* if (state->config->opt != 'p') */
1683
1684 /* Only continue if we didn't abort part way through */
1685 if (is_running)
1686 {
1687 /* Get the footer */
1688 ret = ShpLoaderGetSQLFooter(state, &footer);
1689 if (ret != SHPLOADEROK)
1690 {
1691 pgui_logf("%s\n", state->message);
1692
1693 if (ret == SHPLOADERERR)
1694 goto import_cleanup;
1695 }
1696
1697 /* Just in case index creation takes a long time, update the progress text */
1698 if (state->config->createindex)
1699 {
1700 gtk_label_set_text(GTK_LABEL(label_progress), _("Creating spatial index..."));
1701
1702 /* Allow GTK events to get a look in */
1703 while (gtk_events_pending())
1704 gtk_main_iteration();
1705 }
1706
1707 /* Send the footer to the server */
1708 ret = pgui_exec(footer);
1709 free(footer);
1710
1711 if (!ret)
1712 goto import_cleanup;
1713 }
1714
1715 import_cleanup:
1716 /* Import has definitely stopped running */
1717 is_running = FALSE;
1718
1719 /* Close the existing connection */
1720 PQfinish(pg_connection);
1721 pg_connection = NULL;
1722
1723 /* If we didn't finish inserting all of the items (and we expected to), an error occurred */
1724 if ((state->config->opt != 'p' && i != ShpLoaderGetRecordCount(state)) || !ret)
1725 pgui_logf(_("Shapefile import failed."));
1726 else
1727 pgui_logf(_("Shapefile import completed."));
1728
1729 /* Free the state object */
1730 ShpLoaderDestroy(state);
1731
1732 /* Tidy up */
1733 if (progress_shapefile)
1734 free(progress_shapefile);
1735
1736 /* Get next entry */
1737 is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
1738 }
1739
1740 /* Import has definitely finished */
1741 is_running = FALSE;
1742
1743 /* Enable the button once again */
1744 gtk_widget_set_sensitive(widget, TRUE);
1745
1746 /* Silly GTK bug means we have to hide and show the button for it to work again! */
1747 gtk_widget_hide(widget);
1748 gtk_widget_show(widget);
1749
1750 /* Hide the progress dialog */
1751 gtk_widget_hide(dialog_progress);
1752
1753 /* Allow GTK events to get a look in */
1754 while (gtk_events_pending())
1755 gtk_main_iteration();
1756
1757 /* Tidy up */
1758 free(connection_string);
1759
1760 return;
1761 }
1762
1763 static void
pgui_action_export(GtkWidget * widget,gpointer data)1764 pgui_action_export(GtkWidget *widget, gpointer data)
1765 {
1766 SHPDUMPERCONFIG *dumper_table_config;
1767 SHPDUMPERSTATE *state;
1768 gint is_valid;
1769 gpointer gptr;
1770 GtkTreeIter iter;
1771 char *output_shapefile, *orig_shapefile;
1772 char progress_text[GUIMSG_LINE_MAXLEN+1];
1773 gchar *folder_path;
1774
1775 int ret, success = FALSE, i = 0;
1776
1777 /* Get the first row of the import list */
1778 is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(export_table_list_store), &iter);
1779 if (!is_valid)
1780 {
1781 pgui_seterr(_("ERROR: You haven't specified any tables to export"));
1782 pgui_raise_error_dialogue();
1783
1784 return;
1785 }
1786
1787 /* Firstly make sure that we can connect to the database - if we can't then there isn't much
1788 point doing anything else... */
1789 if (!connection_test())
1790 {
1791 pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
1792 pgui_raise_error_dialogue();
1793
1794 /* Open the connections UI for the user */
1795 update_conn_ui_from_conn_config();
1796
1797 gtk_widget_show_all(GTK_WIDGET(window_conn));
1798 return;
1799 }
1800
1801 /* Now open the file selector dialog so the user can specify where they would like the output
1802 files to reside */
1803 if (gtk_dialog_run(GTK_DIALOG(dialog_folderchooser)) != GTK_RESPONSE_ACCEPT)
1804 {
1805 gtk_widget_hide(dialog_folderchooser);
1806
1807 return;
1808 }
1809
1810 gtk_widget_hide(dialog_folderchooser);
1811 folder_path = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog_folderchooser));
1812
1813 /* Now everything is set up, let's extract the tables */
1814 is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(export_table_list_store), &iter);
1815 while (is_valid)
1816 {
1817 /* Grab the SHPDUMPERCONFIG for this row */
1818 gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
1819 dumper_table_config = (SHPDUMPERCONFIG *)gptr;
1820
1821 pgui_logf("\n==============================");
1822 pgui_logf("Exporting with configuration: %s, %s, %s", dumper_table_config->table, dumper_table_config->schema, dumper_table_config->shp_file);
1823
1824 /* Export is running */
1825 is_running = TRUE;
1826 success = FALSE;
1827
1828 /* Disable the button to prevent multiple imports running at the same time */
1829 gtk_widget_set_sensitive(widget, FALSE);
1830
1831 /* Allow GTK events to get a look in */
1832 while (gtk_events_pending())
1833 gtk_main_iteration();
1834
1835 /* Create the state for each configuration */
1836 state = ShpDumperCreate(dumper_table_config);
1837 state->config->conn = conn;
1838
1839 /* Save the original shapefile name, then create a temporary version containing the full path */
1840 orig_shapefile = dumper_table_config->shp_file;
1841 output_shapefile = malloc(strlen(folder_path) + strlen(dumper_table_config->shp_file) + 2);
1842 strcpy(output_shapefile, folder_path);
1843 strcat(output_shapefile, G_DIR_SEPARATOR_S);
1844 strcat(output_shapefile, dumper_table_config->shp_file);
1845
1846 dumper_table_config->shp_file = output_shapefile;
1847
1848 /* Connect to the database */
1849 ret = ShpDumperConnectDatabase(state);
1850 if (ret != SHPDUMPEROK)
1851 {
1852 pgui_seterr("%s", state->message);
1853 pgui_raise_error_dialogue();
1854
1855 goto export_cleanup;
1856 }
1857
1858 /* Display the progress dialog */
1859 gtk_label_set_text(GTK_LABEL(label_progress), _("Initialising..."));
1860 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.0);
1861 gtk_widget_show_all(dialog_progress);
1862
1863 ret = ShpDumperOpenTable(state);
1864 if (ret != SHPDUMPEROK)
1865 {
1866 pgui_logf("%s", state->message);
1867
1868 if (ret == SHPDUMPERERR)
1869 {
1870 gtk_widget_hide(dialog_progress);
1871
1872 pgui_seterr("%s", state->message);
1873 pgui_raise_error_dialogue();
1874
1875 goto export_cleanup;
1876 }
1877 }
1878
1879 /* Update the text */
1880 snprintf(progress_text, GUIMSG_LINE_MAXLEN, _("Exporting table %s (%d records)..."), dumper_table_config->table, ShpDumperGetRecordCount(state));
1881 progress_text[GUIMSG_LINE_MAXLEN] = '\0';
1882 gtk_label_set_text(GTK_LABEL(label_progress), progress_text);
1883
1884 /* Allow GTK events to get a look in */
1885 while (gtk_events_pending())
1886 gtk_main_iteration();
1887
1888 pgui_logf(_("Done (postgis major version: %d)"), state->pgis_major_version);
1889 pgui_logf(_("Output shape: %s"), shapetypename(state->outshptype));
1890
1891 for (i = 0; i < ShpDumperGetRecordCount(state) && is_running == TRUE; i++)
1892 {
1893 ret = ShpLoaderGenerateShapeRow(state);
1894 if (ret != SHPDUMPEROK)
1895 {
1896 pgui_logf("%s", state->message);
1897
1898 if (ret == SHPDUMPERERR)
1899 {
1900 gtk_widget_hide(dialog_progress);
1901
1902 pgui_seterr("%s", state->message);
1903 pgui_raise_error_dialogue();
1904
1905 goto export_cleanup;
1906 }
1907 }
1908
1909 /* Update the progress bar */
1910 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), (float)i / ShpDumperGetRecordCount(state));
1911
1912 /* Allow GTK events to get a look in */
1913 while (gtk_events_pending())
1914 gtk_main_iteration();
1915 }
1916
1917 /* Finish the dump */
1918 ret = ShpDumperCloseTable(state);
1919 if (ret != SHPDUMPEROK)
1920 {
1921 pgui_logf("%s", state->message);
1922
1923 if (ret == SHPDUMPERERR)
1924 {
1925 gtk_widget_hide(dialog_progress);
1926
1927 pgui_seterr("%s", state->message);
1928 pgui_raise_error_dialogue();
1929 }
1930 }
1931
1932 /* Indicate success */
1933 if (is_running)
1934 success = TRUE;
1935
1936 export_cleanup:
1937
1938 /* Tidy up everything */
1939 ShpDumperDestroy(state);
1940
1941 /* Reset shapefile back to original form (without full path) */
1942 dumper_table_config->shp_file = orig_shapefile;
1943
1944 /* Get next entry */
1945 is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(export_table_list_store), &iter);
1946 }
1947
1948 /* Export has definitely finished */
1949 is_running = FALSE;
1950 if (!success)
1951 pgui_logf(_("Table export failed."));
1952 else
1953 pgui_logf(_("Table export completed."));
1954
1955 /* Enable the button once again */
1956 gtk_widget_set_sensitive(widget, TRUE);
1957
1958 /* Silly GTK bug means we have to hide and show the button for it to work again! */
1959 gtk_widget_hide(widget);
1960 gtk_widget_show(widget);
1961
1962 /* Hide the progress dialog */
1963 gtk_widget_hide(dialog_progress);
1964
1965 /* Allow GTK events to get a look in */
1966 while (gtk_events_pending())
1967 gtk_main_iteration();
1968
1969 return;
1970 }
1971
1972
1973 /* === Import ListView functions and signal handlers === */
1974
1975 /* Creates a single file row in the list table given the URI of a file */
1976 static void
process_single_uri(char * uri)1977 process_single_uri(char *uri)
1978 {
1979 SHPLOADERCONFIG *loader_file_config;
1980 char *filename = NULL;
1981 char *hostname;
1982 GError *error = NULL;
1983
1984 if (uri == NULL)
1985 {
1986 pgui_logf(_("Unable to process drag URI."));
1987 return;
1988 }
1989
1990 filename = g_filename_from_uri(uri, &hostname, &error);
1991 g_free(uri);
1992
1993 if (filename == NULL)
1994 {
1995 pgui_logf(_("Unable to process filename: %s\n"), error->message);
1996 g_error_free(error);
1997 return;
1998 }
1999
2000 /* Create a new row in the listview */
2001 loader_file_config = create_new_file_config(filename);
2002 add_loader_file_config_to_list(loader_file_config);
2003
2004 g_free(filename);
2005 g_free(hostname);
2006
2007 }
2008
2009 /* Update the SHPLOADERCONFIG to the values currently contained within the iter */
2010 static void
update_loader_file_config_from_listview_iter(GtkTreeIter * iter,SHPLOADERCONFIG * loader_file_config)2011 update_loader_file_config_from_listview_iter(GtkTreeIter *iter, SHPLOADERCONFIG *loader_file_config)
2012 {
2013 gchar *schema, *table, *geo_col, *srid;
2014
2015 /* Grab the main values for this file */
2016 gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), iter,
2017 IMPORT_SCHEMA_COLUMN, &schema,
2018 IMPORT_TABLE_COLUMN, &table,
2019 IMPORT_GEOMETRY_COLUMN, &geo_col,
2020 IMPORT_SRID_COLUMN, &srid,
2021 -1);
2022
2023 /* Update the schema */
2024 if (loader_file_config->schema)
2025 free(loader_file_config->schema);
2026
2027 loader_file_config->schema = strdup(schema);
2028
2029 /* Update the table */
2030 if (loader_file_config->table)
2031 free(loader_file_config->table);
2032
2033 loader_file_config->table = strdup(table);
2034
2035 /* Update the geo column */
2036 if (loader_file_config->geo_col)
2037 free(loader_file_config->geo_col);
2038
2039 loader_file_config->geo_col = strdup(geo_col);
2040
2041 /* Update the SRID */
2042 loader_file_config->sr_id = atoi(srid);
2043
2044 /* Free the values */
2045 return;
2046 }
2047
2048
2049 /*
2050 * Here lives the magic of the drag-n-drop of the app. We really don't care
2051 * about much of the provided tidbits. We only actually user selection_data
2052 * and extract a list of filenames from it.
2053 */
2054 static void
pgui_action_handle_file_drop(GtkWidget * widget,GdkDragContext * dc,gint x,gint y,GtkSelectionData * selection_data,guint info,guint t,gpointer data)2055 pgui_action_handle_file_drop(GtkWidget *widget,
2056 GdkDragContext *dc,
2057 gint x, gint y,
2058 GtkSelectionData *selection_data,
2059 guint info, guint t, gpointer data)
2060 {
2061 const gchar *p, *q;
2062
2063 if (selection_data->data == NULL)
2064 {
2065 pgui_logf(_("Unable to process drag data."));
2066 return;
2067 }
2068
2069 p = (char*)selection_data->data;
2070 while (p)
2071 {
2072 /* Only process non-comments */
2073 if (*p != '#')
2074 {
2075 /* Trim leading whitespace */
2076 while (g_ascii_isspace(*p))
2077 p++;
2078 q = p;
2079 /* Scan to the end of the string (null or newline) */
2080 while (*q && (*q != '\n') && (*q != '\r'))
2081 q++;
2082 if (q > p)
2083 {
2084 /* Ignore terminating character */
2085 q--;
2086 /* Trim trailing whitespace */
2087 while (q > p && g_ascii_isspace(*q))
2088 q--;
2089 if (q > p)
2090 {
2091 process_single_uri(g_strndup(p, q - p + 1));
2092 }
2093 }
2094 }
2095 /* Skip to the next entry */
2096 p = strchr(p, '\n');
2097 if (p)
2098 p++;
2099 }
2100 }
2101
2102
2103 /*
2104 * This function is a signal handler for the load mode combo boxes.
2105 */
2106 static void
pgui_action_handle_tree_combo(GtkCellRendererCombo * combo,gchar * path_string,GtkTreeIter * new_iter,gpointer user_data)2107 pgui_action_handle_tree_combo(GtkCellRendererCombo *combo,
2108 gchar *path_string,
2109 GtkTreeIter *new_iter,
2110 gpointer user_data)
2111 {
2112 GtkTreeIter iter;
2113 SHPLOADERCONFIG *loader_file_config;
2114 char opt;
2115 gchar *combo_text;
2116 gpointer gptr;
2117
2118 /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
2119 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path_string);
2120 gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
2121 loader_file_config = (SHPLOADERCONFIG *)gptr;
2122
2123 /* Now grab the row selected within the combo box */
2124 gtk_tree_model_get(GTK_TREE_MODEL(loader_mode_combo_list), new_iter, LOADER_MODE_COMBO_OPTION_CHAR, &opt, -1);
2125
2126 /* Update the configuration */
2127
2128 /* Hack for index creation: we must disable it if we are appending, otherwise we
2129 end up trying to generate the index again */
2130 loader_file_config->createindex = global_loader_config->createindex;
2131
2132 switch (opt)
2133 {
2134 case 'a':
2135 loader_file_config->opt = 'a';
2136
2137 /* Other half of index creation hack */
2138 loader_file_config->createindex = 0;
2139
2140 break;
2141
2142 case 'd':
2143 loader_file_config->opt = 'd';
2144 break;
2145
2146 case 'p':
2147 loader_file_config->opt = 'p';
2148 break;
2149
2150 case 'c':
2151 loader_file_config->opt = 'c';
2152 break;
2153 }
2154
2155 /* Update the selection in the listview with the text from the combo */
2156 gtk_tree_model_get(GTK_TREE_MODEL(loader_mode_combo_list), new_iter, LOADER_MODE_COMBO_TEXT, &combo_text, -1);
2157 gtk_list_store_set(import_file_list_store, &iter, IMPORT_MODE_COLUMN, combo_text, -1);
2158
2159 return;
2160 }
2161
2162
2163 /*
2164 * This method is a signal listener for all text renderers in the file
2165 * list table, including the empty ones. Edits of the empty table are
2166 * passed to an appropriate function, while edits of existing file rows
2167 * are applied and the various validations called.
2168 */
2169 static void
pgui_action_handle_loader_edit(GtkCellRendererText * renderer,gchar * path,gchar * new_text,gpointer column)2170 pgui_action_handle_loader_edit(GtkCellRendererText *renderer,
2171 gchar *path,
2172 gchar *new_text,
2173 gpointer column)
2174 {
2175 GtkTreeIter iter;
2176 gpointer gptr;
2177 gint columnindex;
2178 SHPLOADERCONFIG *loader_file_config;
2179 #define MAXLEN 16
2180 char srid[MAXLEN+1];
2181
2182 /* Empty doesn't fly */
2183 if (strlen(new_text) == 0)
2184 return;
2185
2186 /* Update the model with the current edit change */
2187 columnindex = *(gint *)column;
2188 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path);
2189 gtk_list_store_set(import_file_list_store, &iter, columnindex, new_text, -1);
2190
2191 /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
2192 gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
2193 loader_file_config = (SHPLOADERCONFIG *)gptr;
2194
2195 /* Update the configuration from the current UI data */
2196 update_loader_file_config_from_listview_iter(&iter, loader_file_config);
2197
2198 /* Now refresh the listview UI row with the new configuration */
2199 if ( MAXLEN+1 <= snprintf(srid, MAXLEN+1, "%d", loader_file_config->sr_id) )
2200 {
2201 pgui_logf("Invalid SRID requiring more than %d digits: %d", MAXLEN, loader_file_config->sr_id);
2202 pgui_raise_error_dialogue();
2203 srid[MAXLEN] = '\0';
2204 }
2205
2206 gtk_list_store_set(import_file_list_store, &iter,
2207 IMPORT_SCHEMA_COLUMN, loader_file_config->schema,
2208 IMPORT_TABLE_COLUMN, loader_file_config->table,
2209 IMPORT_GEOMETRY_COLUMN, loader_file_config->geo_col,
2210 IMPORT_SRID_COLUMN, srid,
2211 -1);
2212
2213 return;
2214 }
2215
2216 /*
2217 * Signal handler for the remove box. Performs no user interaction, simply
2218 * removes the row from the table.
2219 */
2220 static void
pgui_action_handle_file_remove(GtkCellRendererToggle * renderer,gchar * path,gpointer user_data)2221 pgui_action_handle_file_remove(GtkCellRendererToggle *renderer,
2222 gchar *path,
2223 gpointer user_data)
2224 {
2225 GtkTreeIter iter;
2226 SHPLOADERCONFIG *loader_file_config;
2227 gpointer gptr;
2228
2229 /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
2230 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path);
2231 gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
2232 loader_file_config = (SHPLOADERCONFIG *)gptr;
2233
2234 /* Free the configuration from memory */
2235 free_loader_config(loader_file_config);
2236
2237 /* Remove the row from the list */
2238 gtk_list_store_remove(import_file_list_store, &iter);
2239
2240 /* Update the filename field width */
2241 update_filename_field_width();
2242 }
2243
2244
2245 /* === Export ListView functions and signal handlers === */
2246
2247 /* Update the SHPDUMPERCONFIG to the values currently contained within the iter */
2248 static void
update_dumper_table_config_from_listview_iter(GtkTreeIter * iter,SHPDUMPERCONFIG * dumper_table_config)2249 update_dumper_table_config_from_listview_iter(GtkTreeIter *iter, SHPDUMPERCONFIG *dumper_table_config)
2250 {
2251 gchar *schema, *table, *geo_col, *filename;
2252
2253 /* Grab the main values for this file */
2254 gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), iter,
2255 EXPORT_SCHEMA_COLUMN, &schema,
2256 EXPORT_TABLE_COLUMN, &table,
2257 EXPORT_GEOMETRY_COLUMN, &geo_col,
2258 EXPORT_FILENAME_COLUMN, &filename,
2259 -1);
2260
2261 /* Update the schema */
2262 if (dumper_table_config->schema)
2263 free(dumper_table_config->schema);
2264
2265 dumper_table_config->schema = strdup(schema);
2266
2267 /* Update the table */
2268 if (dumper_table_config->table)
2269 free(dumper_table_config->table);
2270
2271 dumper_table_config->table = strdup(table);
2272
2273 /* Update the geometry column */
2274 if (dumper_table_config->geo_col_name)
2275 free(dumper_table_config->geo_col_name);
2276
2277 dumper_table_config->geo_col_name = strdup(geo_col);
2278
2279 /* Update the filename column (default to table name) */
2280 if (dumper_table_config->shp_file)
2281 free(dumper_table_config->shp_file);
2282
2283 dumper_table_config->shp_file = strdup(filename);
2284
2285 return;
2286 }
2287
2288 static void
pgui_action_handle_table_geocol_combo(GtkCellRendererCombo * combo,gchar * path_string,GtkTreeIter * new_iter,gpointer user_data)2289 pgui_action_handle_table_geocol_combo(GtkCellRendererCombo *combo,
2290 gchar *path_string,
2291 GtkTreeIter *new_iter,
2292 gpointer user_data)
2293 {
2294 SHPDUMPERCONFIG *dumper_table_config;
2295 gchar *geocol_name;
2296 GtkTreeIter iter;
2297 GtkListStore *model;
2298 gpointer gptr;
2299
2300 /* Get the existing geo column name */
2301 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path_string);
2302 gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter,
2303 EXPORT_POINTER_COLUMN, &gptr,
2304 EXPORT_GEOMETRY_COLUMN, &geocol_name,
2305 EXPORT_GEOMETRY_LISTSTORE_COLUMN, &model,
2306 -1);
2307
2308 /* If the geocol_name is NULL then there was no geo column so exit */
2309 if (!geocol_name)
2310 return;
2311
2312 /* Otherwise update the geo column name in the config and the model */
2313 gtk_tree_model_get(GTK_TREE_MODEL(model), new_iter, TABLECHOOSER_GEOCOL_COMBO_TEXT, &geocol_name, -1);
2314 dumper_table_config = (SHPDUMPERCONFIG *)gptr;
2315
2316 if (dumper_table_config->geo_col_name)
2317 {
2318 free(dumper_table_config->geo_col_name);
2319
2320 dumper_table_config->geo_col_name = strdup(geocol_name);
2321 }
2322
2323 gtk_list_store_set(export_table_list_store, &iter,
2324 EXPORT_GEOMETRY_COLUMN, geocol_name,
2325 -1);
2326
2327 return;
2328 }
2329
2330 static void
pgui_action_handle_dumper_edit(GtkCellRendererText * renderer,gchar * path,gchar * new_text,gpointer column)2331 pgui_action_handle_dumper_edit(GtkCellRendererText *renderer,
2332 gchar *path,
2333 gchar *new_text,
2334 gpointer column)
2335 {
2336 GtkTreeIter iter;
2337 gpointer gptr;
2338 gint columnindex;
2339 SHPDUMPERCONFIG *dumper_table_config;
2340
2341 /* Empty doesn't fly */
2342 if (strlen(new_text) == 0)
2343 return;
2344
2345 /* Update the model with the current edit change */
2346 columnindex = *(gint *)column;
2347 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path);
2348 gtk_list_store_set(export_table_list_store, &iter, columnindex, new_text, -1);
2349
2350 /* Grab the SHPDUMPERCONFIG from the EXPORT_POINTER_COLUMN for the list store */
2351 gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
2352 dumper_table_config = (SHPDUMPERCONFIG *)gptr;
2353
2354 /* Update the configuration from the current UI data */
2355 update_dumper_table_config_from_listview_iter(&iter, dumper_table_config);
2356
2357 /* Now refresh the listview UI row with the new configuration */
2358 gtk_list_store_set(export_table_list_store, &iter,
2359 EXPORT_SCHEMA_COLUMN, dumper_table_config->schema,
2360 EXPORT_TABLE_COLUMN, dumper_table_config->table,
2361 EXPORT_GEOMETRY_COLUMN, dumper_table_config->geo_col_name,
2362 EXPORT_FILENAME_COLUMN, dumper_table_config->shp_file,
2363 -1);
2364
2365 return;
2366 }
2367
2368 /* === Connection Window functions === */
2369
2370 /* Set the connection details UI from the current configuration */
2371 static void
update_conn_ui_from_conn_config(void)2372 update_conn_ui_from_conn_config(void)
2373 {
2374 if (conn->username)
2375 gtk_entry_set_text(GTK_ENTRY(entry_pg_user), conn->username);
2376 else
2377 gtk_entry_set_text(GTK_ENTRY(entry_pg_user), "");
2378
2379 if (conn->password)
2380 gtk_entry_set_text(GTK_ENTRY(entry_pg_pass), conn->password);
2381 else
2382 gtk_entry_set_text(GTK_ENTRY(entry_pg_pass), "");
2383
2384 if (conn->host)
2385 gtk_entry_set_text(GTK_ENTRY(entry_pg_host), conn->host);
2386 else
2387 gtk_entry_set_text(GTK_ENTRY(entry_pg_host), "");
2388
2389 if (conn->port)
2390 gtk_entry_set_text(GTK_ENTRY(entry_pg_port), conn->port);
2391 else
2392 gtk_entry_set_text(GTK_ENTRY(entry_pg_port), "");
2393
2394 if (conn->database)
2395 gtk_entry_set_text(GTK_ENTRY(entry_pg_db), conn->database);
2396 else
2397 gtk_entry_set_text(GTK_ENTRY(entry_pg_db), "");
2398
2399 return;
2400 }
2401
2402 /* Set the current connection configuration from the connection details UI */
2403 static void
update_conn_config_from_conn_ui(void)2404 update_conn_config_from_conn_ui(void)
2405 {
2406 const char *text;
2407
2408 text = gtk_entry_get_text(GTK_ENTRY(entry_pg_user));
2409 if (conn->username)
2410 free(conn->username);
2411
2412 if (strlen(text))
2413 conn->username = strdup(text);
2414 else
2415 conn->username = NULL;
2416
2417 text = gtk_entry_get_text(GTK_ENTRY(entry_pg_pass));
2418 if (conn->password)
2419 free(conn->password);
2420
2421 if (strlen(text))
2422 conn->password = strdup(text);
2423 else
2424 conn->password = NULL;
2425
2426 text = gtk_entry_get_text(GTK_ENTRY(entry_pg_host));
2427 if (conn->host)
2428 free(conn->host);
2429
2430 if (strlen(text))
2431 conn->host = strdup(text);
2432 else
2433 conn->host = NULL;
2434
2435 text = gtk_entry_get_text(GTK_ENTRY(entry_pg_port));
2436 if (conn->port)
2437 free(conn->port);
2438
2439 if (strlen(text))
2440 conn->port = strdup(text);
2441 else
2442 conn->port = NULL;
2443
2444 text = gtk_entry_get_text(GTK_ENTRY(entry_pg_db));
2445 if (conn->database)
2446 free(conn->database);
2447
2448 if (strlen(text))
2449 conn->database = strdup(text);
2450 else
2451 conn->database = NULL;
2452
2453 return;
2454 }
2455
2456 /*
2457 * Open the connection details dialog
2458 */
2459 static void
pgui_action_connection_details(GtkWidget * widget,gpointer data)2460 pgui_action_connection_details(GtkWidget *widget, gpointer data)
2461 {
2462 /* Update the UI with the current options */
2463 update_conn_ui_from_conn_config();
2464
2465 gtk_widget_show_all(GTK_WIDGET(window_conn));
2466 return;
2467 }
2468
2469 /* Validate the connection, returning true or false */
2470 static int
pgui_validate_connection()2471 pgui_validate_connection()
2472 {
2473 int i;
2474
2475 if (conn->port && strlen(conn->port))
2476 {
2477 for (i = 0; i < strlen(conn->port); i++)
2478 {
2479 if (!isdigit(conn->port[i]))
2480 {
2481 pgui_seterr(_("The connection port must be numeric!"));
2482 return 0;
2483 }
2484 }
2485 }
2486
2487 return 1;
2488 }
2489
2490 static void
pgui_sanitize_connection_string(char * connection_string)2491 pgui_sanitize_connection_string(char *connection_string)
2492 {
2493 char *ptr = strstr(connection_string, "password");
2494 if ( ptr )
2495 {
2496 ptr += 10;
2497 while ( *ptr != '\'' && *ptr != '\0' )
2498 {
2499 /* If we find a \, hide both it and the next character */
2500 if ( *ptr == '\\' )
2501 *ptr++ = '*';
2502
2503 *ptr++ = '*';
2504 }
2505 }
2506 return;
2507 }
2508
2509 /*
2510 * We retain the ability to explicitly request a test of the connection
2511 * parameters. This is the button signal handler to do so.
2512 */
2513 static void
pgui_action_connection_okay(GtkWidget * widget,gpointer data)2514 pgui_action_connection_okay(GtkWidget *widget, gpointer data)
2515 {
2516 /* Update the configuration structure from the form */
2517 update_conn_config_from_conn_ui();
2518
2519 /* Make sure have a valid connection first */
2520 if (!pgui_validate_connection())
2521 {
2522 pgui_raise_error_dialogue();
2523 return;
2524 }
2525
2526 if (!connection_test())
2527 {
2528 pgui_logf(_("Connection failed."));
2529
2530 /* If the connection failed, display a warning before closing */
2531 pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
2532 pgui_raise_error_dialogue();
2533 }
2534 else
2535 {
2536 pgui_logf(_("Connection succeeded."));
2537 }
2538
2539
2540 /* Hide the window after the test */
2541 gtk_widget_hide(GTK_WIDGET(window_conn));
2542 }
2543
2544
2545 /* === Window creation functions === */
2546
2547 static void
pgui_create_about_dialog(void)2548 pgui_create_about_dialog(void)
2549 {
2550 const char *authors[] =
2551 {
2552 "Paul Ramsey <pramsey@cleverelephant.ca>",
2553 "Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>",
2554 "Mark Leslie <mark.s.leslie@gmail.com>",
2555 NULL
2556 };
2557
2558
2559
2560 dialog_about = gtk_about_dialog_new();
2561 gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog_about), _("PostGIS Shapefile Import/Export Manager"));
2562 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog_about), POSTGIS_LIB_VERSION);
2563 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog_about), "http://postgis.net/");
2564 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(dialog_about), authors);
2565 }
2566
2567 static void
pgui_create_filechooser_dialog(void)2568 pgui_create_filechooser_dialog(void)
2569 {
2570 GtkFileFilter *file_filter_shape;
2571
2572 /* Create the dialog */
2573 dialog_filechooser = gtk_file_chooser_dialog_new( _("Select a Shape File"), GTK_WINDOW (window_main),
2574 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
2575
2576 /* Filter for .shp files */
2577 file_filter_shape = gtk_file_filter_new();
2578 gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.shp");
2579 gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("Shape Files (*.shp)"));
2580 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_filechooser), file_filter_shape);
2581
2582 /* Filter for .dbf files */
2583 file_filter_shape = gtk_file_filter_new();
2584 gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.dbf");
2585 gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("DBF Files (*.dbf)"));
2586 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_filechooser), file_filter_shape);
2587
2588 /* Allow multiple files to be selected */
2589 g_object_set(dialog_filechooser, "select-multiple", TRUE, NULL);
2590
2591 return;
2592 }
2593
2594 static void
pgui_create_folderchooser_dialog(void)2595 pgui_create_folderchooser_dialog(void)
2596 {
2597 GtkFileFilter *file_filter_shape;
2598
2599 /* Create the dialog */
2600 dialog_folderchooser = gtk_file_chooser_dialog_new( _("Select an output folder"), GTK_WINDOW (window_main),
2601 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
2602
2603 /* Filter for .shp files */
2604 file_filter_shape = gtk_file_filter_new();
2605 gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.shp");
2606 gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("Shape Files (*.shp)"));
2607 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_folderchooser), file_filter_shape);
2608
2609 /* Filter for .dbf files */
2610 file_filter_shape = gtk_file_filter_new();
2611 gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.dbf");
2612 gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("DBF Files (*.dbf)"));
2613 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_folderchooser), file_filter_shape);
2614
2615 return;
2616 }
2617
2618 static void
pgui_create_progress_dialog()2619 pgui_create_progress_dialog()
2620 {
2621 GtkWidget *vbox_progress, *table_progress;
2622
2623 dialog_progress = gtk_dialog_new_with_buttons(_("Working..."), GTK_WINDOW(window_main), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
2624
2625 gtk_window_set_modal(GTK_WINDOW(dialog_progress), TRUE);
2626 gtk_window_set_keep_above(GTK_WINDOW(dialog_progress), TRUE);
2627 gtk_window_set_default_size(GTK_WINDOW(dialog_progress), 640, -1);
2628
2629 /* Use a vbox as the base container */
2630 vbox_progress = gtk_dialog_get_content_area(GTK_DIALOG(dialog_progress));
2631 gtk_box_set_spacing(GTK_BOX(vbox_progress), 15);
2632
2633 /* Create a table within the vbox */
2634 table_progress = gtk_table_new(2, 1, TRUE);
2635 gtk_container_set_border_width (GTK_CONTAINER (table_progress), 12);
2636 gtk_table_set_row_spacings(GTK_TABLE(table_progress), 5);
2637 gtk_table_set_col_spacings(GTK_TABLE(table_progress), 10);
2638
2639 /* Text for the progress bar */
2640 label_progress = gtk_label_new("");
2641 gtk_table_attach_defaults(GTK_TABLE(table_progress), label_progress, 0, 1, 0, 1);
2642
2643 /* Progress bar for the import */
2644 progress = gtk_progress_bar_new();
2645 gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(progress), GTK_PROGRESS_LEFT_TO_RIGHT);
2646 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.0);
2647 gtk_table_attach_defaults(GTK_TABLE(table_progress), progress, 0, 1, 1, 2);
2648
2649 /* Add the table to the vbox */
2650 gtk_box_pack_start(GTK_BOX(vbox_progress), table_progress, FALSE, FALSE, 0);
2651
2652 /* Add signal for cancel button */
2653 g_signal_connect(dialog_progress, "response", G_CALLBACK(pgui_action_progress_cancel), dialog_progress);
2654
2655 /* Make sure we catch a delete event too */
2656 gtk_signal_connect(GTK_OBJECT(dialog_progress), "delete_event", GTK_SIGNAL_FUNC(pgui_action_progress_delete), NULL);
2657
2658 return;
2659 }
2660
2661 static void
pgui_create_options_dialog_add_label(GtkWidget * table,const char * str,gfloat alignment,int row)2662 pgui_create_options_dialog_add_label(GtkWidget *table, const char *str, gfloat alignment, int row)
2663 {
2664 GtkWidget *align = gtk_alignment_new(alignment, 0.5, 0.0, 1.0);
2665 GtkWidget *label = gtk_label_new(str);
2666 gtk_table_attach_defaults(GTK_TABLE(table), align, 1, 3, row, row + 1);
2667 gtk_container_add(GTK_CONTAINER (align), label);
2668 }
2669
2670 static void
pgui_create_loader_options_dialog()2671 pgui_create_loader_options_dialog()
2672 {
2673 GtkWidget *table_options;
2674 GtkWidget *align_options_center;
2675 static int text_width = 12;
2676
2677 dialog_loader_options = gtk_dialog_new_with_buttons(_("Import Options"), GTK_WINDOW(window_main), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
2678
2679 gtk_window_set_modal (GTK_WINDOW(dialog_loader_options), TRUE);
2680 gtk_window_set_keep_above (GTK_WINDOW(dialog_loader_options), TRUE);
2681 gtk_window_set_default_size (GTK_WINDOW(dialog_loader_options), 180, -1);
2682
2683 table_options = gtk_table_new(7, 3, TRUE);
2684 gtk_container_set_border_width (GTK_CONTAINER (table_options), 12);
2685 gtk_table_set_row_spacings(GTK_TABLE(table_options), 5);
2686 gtk_table_set_col_spacings(GTK_TABLE(table_options), 10);
2687
2688 pgui_create_options_dialog_add_label(table_options, _("DBF file character encoding"), 0.0, 0);
2689 entry_options_encoding = gtk_entry_new();
2690 gtk_entry_set_width_chars(GTK_ENTRY(entry_options_encoding), text_width);
2691 gtk_table_attach_defaults(GTK_TABLE(table_options), entry_options_encoding, 0, 1, 0, 1 );
2692
2693 pgui_create_options_dialog_add_label(table_options, _("Preserve case of column names"), 0.0, 1);
2694 checkbutton_loader_options_preservecase = gtk_check_button_new();
2695 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2696 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 1, 2 );
2697 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_preservecase);
2698
2699 pgui_create_options_dialog_add_label(table_options, _("Do not create 'bigint' columns"), 0.0, 2);
2700 checkbutton_loader_options_forceint = gtk_check_button_new();
2701 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2702 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 2, 3 );
2703 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_forceint);
2704
2705 pgui_create_options_dialog_add_label(table_options, _("Create spatial index automatically after load"), 0.0, 3);
2706 checkbutton_loader_options_autoindex = gtk_check_button_new();
2707 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2708 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 3, 4 );
2709 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_autoindex);
2710
2711 pgui_create_options_dialog_add_label(table_options, _("Load only attribute (dbf) data"), 0.0, 4);
2712 checkbutton_loader_options_dbfonly = gtk_check_button_new();
2713 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2714 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 4, 5 );
2715 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_dbfonly);
2716
2717 pgui_create_options_dialog_add_label(table_options, _("Load data using COPY rather than INSERT"), 0.0, 5);
2718 checkbutton_loader_options_dumpformat = gtk_check_button_new();
2719 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 0.0 );
2720 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 5, 6 );
2721 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_dumpformat);
2722
2723 pgui_create_options_dialog_add_label(table_options, _("Load into GEOGRAPHY column"), 0.0, 6);
2724 checkbutton_loader_options_geography = gtk_check_button_new();
2725 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2726 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 6, 7 );
2727 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_geography);
2728
2729 pgui_create_options_dialog_add_label(table_options, _("Generate simple geometries instead of MULTI geometries"), 0.0, 7);
2730 checkbutton_loader_options_simplegeoms = gtk_check_button_new();
2731 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2732 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 7, 8 );
2733 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_simplegeoms);
2734
2735 /* Catch the response from the dialog */
2736 g_signal_connect(dialog_loader_options, "response", G_CALLBACK(pgui_action_loader_options_close), dialog_loader_options);
2737 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_loader_options)->vbox), table_options, FALSE, FALSE, 0);
2738
2739 /* Hook the delete event so we don't destroy the dialog (just hide) if cancelled */
2740 gtk_signal_connect(GTK_OBJECT(dialog_loader_options), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
2741 }
2742
2743 static void
pgui_create_dumper_options_dialog()2744 pgui_create_dumper_options_dialog()
2745 {
2746 GtkWidget *table_options;
2747 GtkWidget *align_options_center;
2748
2749 dialog_dumper_options = gtk_dialog_new_with_buttons(_("Export Options"), GTK_WINDOW(window_main), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
2750
2751 gtk_window_set_modal (GTK_WINDOW(dialog_dumper_options), TRUE);
2752 gtk_window_set_keep_above (GTK_WINDOW(dialog_dumper_options), TRUE);
2753 gtk_window_set_default_size (GTK_WINDOW(dialog_dumper_options), 180, -1);
2754
2755 table_options = gtk_table_new(3, 3, TRUE);
2756 gtk_container_set_border_width (GTK_CONTAINER (table_options), 12);
2757 gtk_table_set_row_spacings(GTK_TABLE(table_options), 5);
2758 gtk_table_set_col_spacings(GTK_TABLE(table_options), 10);
2759
2760 pgui_create_options_dialog_add_label(table_options, _("Include gid column in the exported table"), 0.0, 0);
2761 checkbutton_dumper_options_includegid = gtk_check_button_new();
2762 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2763 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 0, 1 );
2764 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_includegid);
2765
2766 pgui_create_options_dialog_add_label(table_options, _("Preserve case of column names"), 0.0, 1);
2767 checkbutton_dumper_options_keep_fieldname_case = gtk_check_button_new();
2768 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2769 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 1, 2 );
2770 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_keep_fieldname_case);
2771
2772 pgui_create_options_dialog_add_label(table_options, _("Escape column names"), 0.0, 2);
2773 checkbutton_dumper_options_unescapedattrs = gtk_check_button_new();
2774 align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2775 gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 2, 3 );
2776 gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_unescapedattrs);
2777
2778 /* Catch the response from the dialog */
2779 g_signal_connect(dialog_dumper_options, "response", G_CALLBACK(pgui_action_dumper_options_close), dialog_dumper_options);
2780 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_dumper_options)->vbox), table_options, FALSE, FALSE, 0);
2781
2782 /* Hook the delete event so we don't destroy the dialog (just hide) if cancelled */
2783 gtk_signal_connect(GTK_OBJECT(dialog_dumper_options), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
2784 }
2785
2786 /*
2787 * This function creates the UI artefacts for the file list table and hooks
2788 * up all the pretty signals.
2789 */
2790 static void
pgui_create_tablechooser_dialog()2791 pgui_create_tablechooser_dialog()
2792 {
2793 GtkWidget *vbox_tree, *table_progress;
2794 GtkWidget *sw, *label;
2795 GtkTreeSelection *chooser_selection;
2796
2797 /* Create the main top level window with a 10px border */
2798 dialog_tablechooser = gtk_dialog_new_with_buttons(_("Table selection"), GTK_WINDOW(window_main),
2799 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
2800
2801 gtk_container_set_border_width(GTK_CONTAINER(dialog_tablechooser), 10);
2802 gtk_window_set_position(GTK_WINDOW(dialog_tablechooser), GTK_WIN_POS_CENTER);
2803
2804 vbox_tree = gtk_dialog_get_content_area(GTK_DIALOG(dialog_tablechooser));
2805
2806 /* Setup a model */
2807 chooser_table_list_store = gtk_list_store_new(TABLECHOOSER_N_COLUMNS,
2808 G_TYPE_STRING,
2809 G_TYPE_STRING,
2810 GTK_TYPE_TREE_MODEL,
2811 G_TYPE_STRING,
2812 G_TYPE_INT);
2813
2814 /* Because we want to do selective filtering on the treeview content, we now implement a GtkTreeModel
2815 filter on top of the original tree model */
2816 chooser_filtered_table_list_store = (GtkListStore *)gtk_tree_model_filter_new(GTK_TREE_MODEL(chooser_table_list_store), NULL);
2817 gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(chooser_filtered_table_list_store),
2818 (GtkTreeModelFilterVisibleFunc)table_chooser_visibility_func, NULL, NULL);
2819
2820 /* Create the view and such */
2821 chooser_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(chooser_filtered_table_list_store));
2822 chooser_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(chooser_tree));
2823 gtk_tree_selection_set_mode(chooser_selection, GTK_SELECTION_MULTIPLE);
2824
2825 /* Make the tree view in a scrollable window */
2826 sw = gtk_scrolled_window_new(NULL, NULL);
2827 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2828 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
2829 gtk_widget_set_size_request(sw, 320, 240);
2830
2831 gtk_box_pack_start(GTK_BOX(vbox_tree), sw, FALSE, FALSE, 10);
2832 gtk_container_add(GTK_CONTAINER(sw), chooser_tree);
2833
2834 /* Schema Field */
2835 chooser_schema_renderer = gtk_cell_renderer_text_new();
2836 g_object_set(chooser_schema_renderer, "editable", TRUE, NULL);
2837 g_signal_connect(G_OBJECT(chooser_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), NULL);
2838 chooser_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
2839 chooser_schema_renderer,
2840 "text",
2841 TABLECHOOSER_SCHEMA_COLUMN,
2842 NULL);
2843 g_object_set(chooser_schema_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
2844 gtk_tree_view_append_column(GTK_TREE_VIEW(chooser_tree), chooser_schema_column);
2845
2846 /* Table Field */
2847 chooser_table_renderer = gtk_cell_renderer_text_new();
2848 g_object_set(chooser_table_renderer, "editable", FALSE, NULL);
2849 g_signal_connect(G_OBJECT(chooser_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), NULL);
2850 chooser_table_column = gtk_tree_view_column_new_with_attributes(_("Table"),
2851 chooser_table_renderer,
2852 "text",
2853 TABLECHOOSER_TABLE_COLUMN,
2854 NULL);
2855 g_object_set(chooser_table_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
2856 gtk_tree_view_append_column(GTK_TREE_VIEW(chooser_tree), chooser_table_column);
2857
2858 /* Create table to hold the tick-box and text */
2859 table_progress = gtk_table_new(1, 2, FALSE);
2860 gtk_container_set_border_width (GTK_CONTAINER (table_progress), 0);
2861 gtk_table_set_row_spacings(GTK_TABLE(table_progress), 0);
2862 gtk_table_set_col_spacings(GTK_TABLE(table_progress), 0);
2863
2864 checkbutton_chooser_geoonly = gtk_check_button_new();
2865 gtk_table_attach(GTK_TABLE(table_progress), checkbutton_chooser_geoonly, 0, 1, 0, 1, GTK_SHRINK, GTK_FILL, 0, 0);
2866 label = gtk_label_new(_("Only show tables with geo columns"));
2867 gtk_table_attach(GTK_TABLE(table_progress), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 5, 0);
2868 g_signal_connect(G_OBJECT(checkbutton_chooser_geoonly), "toggled", G_CALLBACK(pgui_action_chooser_toggle_show_geocolumn), NULL);
2869 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_chooser_geoonly), TRUE);
2870
2871 /* Attach table to the vbox */
2872 gtk_box_pack_start(GTK_BOX(vbox_tree), table_progress, FALSE, FALSE, 10);
2873
2874 return;
2875 }
2876
2877
2878 /*
2879 * This function creates the UI artefacts for the file list table and hooks
2880 * up all the pretty signals.
2881 */
2882 static void
pgui_create_import_file_table(GtkWidget * import_list_frame)2883 pgui_create_import_file_table(GtkWidget *import_list_frame)
2884 {
2885 GtkWidget *vbox_tree;
2886 GtkWidget *sw;
2887 GtkTreeIter iter;
2888 gint *column_indexes;
2889
2890 gtk_container_set_border_width (GTK_CONTAINER (import_list_frame), 0);
2891
2892 vbox_tree = gtk_vbox_new(FALSE, 15);
2893 gtk_container_set_border_width(GTK_CONTAINER(vbox_tree), 5);
2894 gtk_container_add(GTK_CONTAINER(import_list_frame), vbox_tree);
2895
2896 /* Setup a model */
2897 import_file_list_store = gtk_list_store_new(IMPORT_N_COLUMNS,
2898 G_TYPE_POINTER,
2899 G_TYPE_STRING,
2900 G_TYPE_STRING,
2901 G_TYPE_STRING,
2902 G_TYPE_STRING,
2903 G_TYPE_STRING,
2904 G_TYPE_STRING,
2905 G_TYPE_BOOLEAN);
2906
2907 /* Create the view and such */
2908 import_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(import_file_list_store));
2909
2910 /* GTK has a slightly brain-dead API in that you can't directly find
2911 the column being used by a GtkCellRenderer when using the same
2912 callback to handle multiple fields; hence we manually store this
2913 information here and pass a pointer to the column index into
2914 the signal handler */
2915 column_indexes = g_malloc(sizeof(gint) * IMPORT_N_COLUMNS);
2916
2917 /* Make the tree view in a scrollable window */
2918 sw = gtk_scrolled_window_new(NULL, NULL);
2919 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2920 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
2921 gtk_widget_set_size_request(sw, -1, 150);
2922
2923 gtk_box_pack_start(GTK_BOX(vbox_tree), sw, TRUE, TRUE, 0);
2924 gtk_container_add(GTK_CONTAINER (sw), import_tree);
2925
2926 /* Place the "Add File" button below the list view */
2927 add_file_button = gtk_button_new_with_label(_("Add File"));
2928 gtk_container_add (GTK_CONTAINER (vbox_tree), add_file_button);
2929
2930 /* Filename Field */
2931 import_filename_renderer = gtk_cell_renderer_text_new();
2932 g_object_set(import_filename_renderer, "editable", FALSE, NULL);
2933 column_indexes[IMPORT_FILENAME_COLUMN] = IMPORT_FILENAME_COLUMN;
2934 g_signal_connect(G_OBJECT(import_filename_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_FILENAME_COLUMN]);
2935 import_filename_column = gtk_tree_view_column_new_with_attributes(_("Shapefile"),
2936 import_filename_renderer,
2937 "text",
2938 IMPORT_FILENAME_COLUMN,
2939 NULL);
2940 g_object_set(import_filename_column, "resizable", TRUE, NULL);
2941 gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_filename_column);
2942
2943 /* Schema Field */
2944 import_schema_renderer = gtk_cell_renderer_text_new();
2945 g_object_set(import_schema_renderer, "editable", TRUE, NULL);
2946 column_indexes[IMPORT_SCHEMA_COLUMN] = IMPORT_SCHEMA_COLUMN;
2947 g_signal_connect(G_OBJECT(import_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_SCHEMA_COLUMN]);
2948 import_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
2949 import_schema_renderer,
2950 "text",
2951 IMPORT_SCHEMA_COLUMN,
2952 NULL);
2953 g_object_set(import_schema_column, "resizable", TRUE, "expand", TRUE, NULL);
2954 gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_schema_column);
2955
2956 /* Table Field */
2957 import_table_renderer = gtk_cell_renderer_text_new();
2958 g_object_set(import_table_renderer, "editable", TRUE, NULL);
2959 column_indexes[IMPORT_TABLE_COLUMN] = IMPORT_TABLE_COLUMN;
2960 g_signal_connect(G_OBJECT(import_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_TABLE_COLUMN]);
2961 import_table_column = gtk_tree_view_column_new_with_attributes(_("Table"),
2962 import_table_renderer,
2963 "text",
2964 IMPORT_TABLE_COLUMN,
2965 NULL);
2966 g_object_set(import_table_column, "resizable", TRUE, "expand", TRUE, NULL);
2967 gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_table_column);
2968
2969 /* Geo column field */
2970 import_geom_column_renderer = gtk_cell_renderer_text_new();
2971 g_object_set(import_geom_column_renderer, "editable", TRUE, NULL);
2972 column_indexes[IMPORT_GEOMETRY_COLUMN] = IMPORT_GEOMETRY_COLUMN;
2973 g_signal_connect(G_OBJECT(import_geom_column_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_GEOMETRY_COLUMN]);
2974 import_geom_column = gtk_tree_view_column_new_with_attributes(_("Geo Column"),
2975 import_geom_column_renderer,
2976 "text",
2977 IMPORT_GEOMETRY_COLUMN,
2978 NULL);
2979 g_object_set(import_geom_column, "resizable", TRUE, "expand", TRUE, NULL);
2980 gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_geom_column);
2981
2982 /* SRID Field */
2983 import_srid_renderer = gtk_cell_renderer_text_new();
2984 g_object_set(import_srid_renderer, "editable", TRUE, NULL);
2985 column_indexes[IMPORT_SRID_COLUMN] = IMPORT_SRID_COLUMN;
2986 g_signal_connect(G_OBJECT(import_srid_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_SRID_COLUMN]);
2987 import_srid_column = gtk_tree_view_column_new_with_attributes("SRID",
2988 import_srid_renderer,
2989 "text",
2990 IMPORT_SRID_COLUMN,
2991 NULL);
2992 g_object_set(import_srid_column, "resizable", TRUE, "expand", TRUE, NULL);
2993 gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_srid_column);
2994
2995 /* Mode Combo Field */
2996 loader_mode_combo_list = gtk_list_store_new(LOADER_MODE_COMBO_COLUMNS,
2997 G_TYPE_STRING,
2998 G_TYPE_CHAR);
2999
3000 gtk_list_store_insert(loader_mode_combo_list, &iter, CREATE_MODE);
3001 gtk_list_store_set(loader_mode_combo_list, &iter,
3002 LOADER_MODE_COMBO_TEXT, _("Create"),
3003 LOADER_MODE_COMBO_OPTION_CHAR, 'c',
3004 -1);
3005 gtk_list_store_insert(loader_mode_combo_list, &iter, APPEND_MODE);
3006 gtk_list_store_set(loader_mode_combo_list, &iter,
3007 LOADER_MODE_COMBO_TEXT, _("Append"),
3008 LOADER_MODE_COMBO_OPTION_CHAR, 'a',
3009 -1);
3010 gtk_list_store_insert(loader_mode_combo_list, &iter, DELETE_MODE);
3011 gtk_list_store_set(loader_mode_combo_list, &iter,
3012 LOADER_MODE_COMBO_TEXT, _("Delete"),
3013 LOADER_MODE_COMBO_OPTION_CHAR, 'd',
3014 -1);
3015 gtk_list_store_insert(loader_mode_combo_list, &iter, PREPARE_MODE);
3016 gtk_list_store_set(loader_mode_combo_list, &iter,
3017 LOADER_MODE_COMBO_TEXT, _("Prepare"),
3018 LOADER_MODE_COMBO_OPTION_CHAR, 'p',
3019 -1);
3020 loader_mode_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(loader_mode_combo_list));
3021 import_mode_renderer = gtk_cell_renderer_combo_new();
3022 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(loader_mode_combo),
3023 import_mode_renderer, TRUE);
3024 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(loader_mode_combo),
3025 import_mode_renderer, "text", 0);
3026 g_object_set(import_mode_renderer,
3027 "model", loader_mode_combo_list,
3028 "editable", TRUE,
3029 "has-entry", FALSE,
3030 "text-column", LOADER_MODE_COMBO_TEXT,
3031 NULL);
3032 import_mode_column = gtk_tree_view_column_new_with_attributes(_("Mode"),
3033 import_mode_renderer,
3034 "text",
3035 IMPORT_MODE_COLUMN,
3036 NULL);
3037 gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_mode_column);
3038 gtk_combo_box_set_active(GTK_COMBO_BOX(loader_mode_combo), 1);
3039 g_object_set(import_mode_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3040
3041 g_signal_connect (G_OBJECT(import_mode_renderer), "changed", G_CALLBACK(pgui_action_handle_tree_combo), NULL);
3042
3043 /* Remove Field */
3044 import_remove_renderer = gtk_cell_renderer_toggle_new();
3045 g_object_set(import_remove_renderer, "activatable", TRUE, NULL);
3046 g_signal_connect(G_OBJECT(import_remove_renderer), "toggled", G_CALLBACK (pgui_action_handle_file_remove), NULL);
3047 import_remove_column = gtk_tree_view_column_new_with_attributes("Rm",
3048 import_remove_renderer, NULL);
3049 g_object_set(import_remove_column, "resizable", TRUE, "expand", FALSE, "fixed-width", 64, "sizing", GTK_TREE_VIEW_COLUMN_FIXED, NULL);
3050 gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_remove_column);
3051
3052 g_signal_connect (G_OBJECT (add_file_button), "clicked", G_CALLBACK (pgui_action_open_file_dialog), NULL);
3053
3054 /* Drag n Drop wiring */
3055 GtkTargetEntry drop_types[] =
3056 {
3057 { "text/uri-list", 0, 0}
3058 };
3059
3060 gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
3061 gtk_drag_dest_set(GTK_WIDGET(import_tree),
3062 GTK_DEST_DEFAULT_ALL,
3063 drop_types, n_drop_types,
3064 GDK_ACTION_COPY);
3065 g_signal_connect(G_OBJECT(import_tree), "drag_data_received",
3066 G_CALLBACK(pgui_action_handle_file_drop), NULL);
3067 }
3068
3069 /*
3070 * This function creates the UI artefacts for the file list table and hooks
3071 * up all the pretty signals.
3072 */
3073 static void
pgui_create_export_table_table(GtkWidget * export_list_frame)3074 pgui_create_export_table_table(GtkWidget *export_list_frame)
3075 {
3076 GtkWidget *vbox_tree;
3077 GtkWidget *sw;
3078 gint *column_indexes;
3079
3080 gtk_container_set_border_width (GTK_CONTAINER (export_list_frame), 0);
3081
3082 vbox_tree = gtk_vbox_new(FALSE, 15);
3083 gtk_container_set_border_width(GTK_CONTAINER(vbox_tree), 5);
3084 gtk_container_add(GTK_CONTAINER(export_list_frame), vbox_tree);
3085
3086 /* Setup a model */
3087 export_table_list_store = gtk_list_store_new(EXPORT_N_COLUMNS,
3088 G_TYPE_POINTER,
3089 G_TYPE_STRING,
3090 G_TYPE_STRING,
3091 G_TYPE_STRING,
3092 GTK_TYPE_TREE_MODEL,
3093 G_TYPE_STRING,
3094 G_TYPE_BOOLEAN);
3095
3096 /* Create the view and such */
3097 export_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(export_table_list_store));
3098
3099 /* GTK has a slightly brain-dead API in that you can't directly find
3100 the column being used by a GtkCellRenderer when using the same
3101 callback to handle multiple fields; hence we manually store this
3102 information here and pass a pointer to the column index into
3103 the signal handler */
3104 column_indexes = g_malloc(sizeof(gint) * EXPORT_N_COLUMNS);
3105
3106 /* Make the tree view in a scrollable window */
3107 sw = gtk_scrolled_window_new(NULL, NULL);
3108 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3109 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
3110 gtk_widget_set_size_request(sw, -1, 150);
3111
3112 gtk_box_pack_start(GTK_BOX(vbox_tree), sw, TRUE, TRUE, 0);
3113 gtk_container_add(GTK_CONTAINER (sw), export_tree);
3114
3115 /* Place the "Add Table" button below the list view */
3116 add_table_button = gtk_button_new_with_label(_("Add Table"));
3117 gtk_container_add (GTK_CONTAINER (vbox_tree), add_table_button);
3118
3119 /* Schema Field */
3120 export_schema_renderer = gtk_cell_renderer_text_new();
3121 g_object_set(export_schema_renderer, "editable", FALSE, NULL);
3122 column_indexes[EXPORT_SCHEMA_COLUMN] = EXPORT_SCHEMA_COLUMN;
3123 g_signal_connect(G_OBJECT(export_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[EXPORT_SCHEMA_COLUMN]);
3124 export_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
3125 export_schema_renderer,
3126 "text",
3127 EXPORT_SCHEMA_COLUMN,
3128 NULL);
3129 g_object_set(export_schema_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3130 gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_schema_column);
3131
3132 /* Table Field */
3133 export_table_renderer = gtk_cell_renderer_text_new();
3134 g_object_set(export_table_renderer, "editable", FALSE, NULL);
3135 column_indexes[EXPORT_TABLE_COLUMN] = EXPORT_TABLE_COLUMN;
3136 g_signal_connect(G_OBJECT(export_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[EXPORT_TABLE_COLUMN]);
3137 export_table_column = gtk_tree_view_column_new_with_attributes(_("Table"),
3138 export_table_renderer,
3139 "text",
3140 EXPORT_TABLE_COLUMN,
3141 NULL);
3142 g_object_set(export_table_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3143 gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_table_column);
3144
3145 /* Geo column field */
3146 export_geom_column_combo = gtk_combo_box_new();
3147 export_geom_column_renderer = gtk_cell_renderer_combo_new();
3148 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(export_geom_column_combo),
3149 export_geom_column_renderer, TRUE);
3150 g_object_set(export_geom_column_renderer,
3151 "editable", TRUE,
3152 "has-entry", FALSE,
3153 "text-column", TABLECHOOSER_GEOCOL_COMBO_TEXT,
3154 NULL);
3155 export_geom_column = gtk_tree_view_column_new_with_attributes(_("Geo Column"),
3156 export_geom_column_renderer,
3157 "model",
3158 EXPORT_GEOMETRY_LISTSTORE_COLUMN,
3159 "text",
3160 EXPORT_GEOMETRY_COLUMN,
3161 NULL);
3162 g_object_set(export_geom_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3163 gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_geom_column);
3164 g_signal_connect (G_OBJECT(export_geom_column_renderer), "changed", G_CALLBACK(pgui_action_handle_table_geocol_combo), NULL);
3165
3166 /* Filename Field */
3167 export_filename_renderer = gtk_cell_renderer_text_new();
3168 g_object_set(export_filename_renderer, "editable", TRUE, NULL);
3169 column_indexes[EXPORT_FILENAME_COLUMN] = EXPORT_FILENAME_COLUMN;
3170 g_signal_connect(G_OBJECT(export_filename_renderer), "edited", G_CALLBACK(pgui_action_handle_dumper_edit), &column_indexes[EXPORT_FILENAME_COLUMN]);
3171 export_filename_column = gtk_tree_view_column_new_with_attributes(_("Filename"),
3172 export_filename_renderer,
3173 "text",
3174 EXPORT_FILENAME_COLUMN,
3175 NULL);
3176 g_object_set(export_filename_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3177 gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_filename_column);
3178
3179 /* Remove Field */
3180 export_remove_renderer = gtk_cell_renderer_toggle_new();
3181 g_object_set(export_remove_renderer, "activatable", TRUE, NULL);
3182 g_signal_connect(G_OBJECT(export_remove_renderer), "toggled", G_CALLBACK (pgui_action_handle_table_remove), NULL);
3183 export_remove_column = gtk_tree_view_column_new_with_attributes("Rm",
3184 export_remove_renderer, NULL);
3185 g_object_set(export_remove_column, "resizable", TRUE, "expand", FALSE, "fixed-width", 64, "sizing", GTK_TREE_VIEW_COLUMN_FIXED, NULL);
3186 gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_remove_column);
3187
3188 g_signal_connect (G_OBJECT (add_table_button), "clicked", G_CALLBACK (pgui_action_open_table_dialog), NULL);
3189 }
3190
3191 static void
pgui_create_connection_window()3192 pgui_create_connection_window()
3193 {
3194 /* Default text width */
3195 static int text_width = 12;
3196
3197 /* Vbox container */
3198 GtkWidget *vbox;
3199
3200 /* Reusable label handle */
3201 GtkWidget *label;
3202
3203 /* PgSQL section */
3204 GtkWidget *frame_pg, *table_pg;
3205
3206 /* OK button */
3207 GtkWidget *button_okay;
3208
3209 /* Create the main top level window with a 10px border */
3210 window_conn = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3211 gtk_container_set_border_width(GTK_CONTAINER(window_conn), 10);
3212 gtk_window_set_title(GTK_WINDOW(window_conn), _("PostGIS connection"));
3213 gtk_window_set_position(GTK_WINDOW(window_conn), GTK_WIN_POS_CENTER);
3214 gtk_window_set_modal(GTK_WINDOW(window_conn), TRUE);
3215
3216 /* Use a vbox as the base container */
3217 vbox = gtk_vbox_new(FALSE, 15);
3218
3219 /*
3220 ** PostGIS info in a table
3221 */
3222 frame_pg = gtk_frame_new(_("PostGIS Connection"));
3223 table_pg = gtk_table_new(5, 3, TRUE);
3224 gtk_container_set_border_width (GTK_CONTAINER (table_pg), 8);
3225 gtk_table_set_col_spacings(GTK_TABLE(table_pg), 7);
3226 gtk_table_set_row_spacings(GTK_TABLE(table_pg), 3);
3227
3228 /* User name row */
3229 label = gtk_label_new(_("Username:"));
3230 entry_pg_user = gtk_entry_new();
3231 gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 0, 1 );
3232 gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_user, 1, 3, 0, 1 );
3233
3234 /* Password row */
3235 label = gtk_label_new(_("Password:"));
3236 entry_pg_pass = gtk_entry_new();
3237 gtk_entry_set_visibility( GTK_ENTRY(entry_pg_pass), FALSE);
3238 gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 1, 2 );
3239 gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_pass, 1, 3, 1, 2 );
3240
3241 /* Host and port row */
3242 label = gtk_label_new(_("Server Host:"));
3243 entry_pg_host = gtk_entry_new();
3244 gtk_entry_set_width_chars(GTK_ENTRY(entry_pg_host), text_width);
3245 gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 2, 3 );
3246 gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_host, 1, 2, 2, 3 );
3247
3248 entry_pg_port = gtk_entry_new();
3249 gtk_entry_set_width_chars(GTK_ENTRY(entry_pg_port), 8);
3250 gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_port, 2, 3, 2, 3 );
3251
3252 /* Database row */
3253 label = gtk_label_new(_("Database:"));
3254 entry_pg_db = gtk_entry_new();
3255 gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 3, 4 );
3256 gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_db, 1, 3, 3, 4 );
3257
3258 /* Add table into containing frame */
3259 gtk_container_add(GTK_CONTAINER(frame_pg), table_pg);
3260
3261 /* Add frame into containing vbox */
3262 gtk_container_add(GTK_CONTAINER(window_conn), vbox);
3263
3264 /* Add the vbox into the window */
3265 gtk_container_add(GTK_CONTAINER(vbox), frame_pg);
3266
3267 /* Create a simple "OK" button for the dialog */
3268 button_okay = gtk_button_new_with_label(_("OK"));
3269 gtk_container_add(GTK_CONTAINER(vbox), button_okay);
3270 g_signal_connect(G_OBJECT(button_okay), "clicked", G_CALLBACK(pgui_action_connection_okay), NULL);
3271
3272 /* Hook the delete event so we don't destroy the dialog (only hide it) if cancelled */
3273 gtk_signal_connect(GTK_OBJECT(window_conn), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
3274
3275 return;
3276 }
3277
3278 static void
pgui_create_main_window(const SHPCONNECTIONCONFIG * conn)3279 pgui_create_main_window(const SHPCONNECTIONCONFIG *conn)
3280 {
3281 /* Main widgets */
3282 GtkWidget *vbox_main, *vbox_loader, *vbox_dumper;
3283
3284 /* PgSQL section */
3285 GtkWidget *frame_pg, *import_list_frame, *export_list_frame, *frame_log;
3286 GtkWidget *button_pg_conn;
3287
3288 /* Notebook */
3289 GtkWidget *notebook;
3290
3291 /* Button section */
3292 GtkWidget *loader_hbox_buttons, *loader_button_options, *loader_button_import, *loader_button_cancel, *loader_button_about;
3293 GtkWidget *dumper_hbox_buttons, *dumper_button_options, *dumper_button_export, *dumper_button_cancel, *dumper_button_about;
3294
3295 /* Log section */
3296 GtkWidget *scrolledwindow_log;
3297
3298 /* Create the main top level window with a 10px border */
3299 window_main = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3300 gtk_container_set_border_width(GTK_CONTAINER(window_main), 10);
3301 gtk_window_set_title(GTK_WINDOW(window_main), _("PostGIS Shapefile Import/Export Manager"));
3302 gtk_window_set_position(GTK_WINDOW(window_main), GTK_WIN_POS_CENTER_ALWAYS);
3303 gtk_window_set_resizable(GTK_WINDOW(window_main), FALSE);
3304
3305 /* Open it a bit wider so that both the label and title show up */
3306 gtk_window_set_default_size(GTK_WINDOW(window_main), 180, 500);
3307
3308 /* Connect the destroy event of the window with our pgui_quit function
3309 * When the window is about to be destroyed we get a notificaiton and
3310 * stop the main GTK loop
3311 */
3312 g_signal_connect(G_OBJECT(window_main), "destroy", G_CALLBACK(pgui_quit), NULL);
3313
3314 /* Connection row */
3315 frame_pg = gtk_frame_new(_("PostGIS Connection"));
3316
3317 /* Test button row */
3318 button_pg_conn = gtk_button_new_with_label(_("View connection details..."));
3319 g_signal_connect(G_OBJECT(button_pg_conn), "clicked", G_CALLBACK(pgui_action_connection_details), NULL);
3320 gtk_container_set_border_width(GTK_CONTAINER(button_pg_conn), 10);
3321 gtk_container_add(GTK_CONTAINER(frame_pg), button_pg_conn);
3322
3323 /*
3324 * GTK Notebook for selecting import/export
3325 */
3326 notebook = gtk_notebook_new();
3327
3328 /*
3329 ** Shape file selector
3330 */
3331 import_list_frame = gtk_frame_new(_("Import List"));
3332 pgui_create_import_file_table(import_list_frame);
3333
3334 /*
3335 ** Row of action buttons
3336 */
3337 loader_hbox_buttons = gtk_hbox_new(TRUE, 15);
3338 gtk_container_set_border_width (GTK_CONTAINER (loader_hbox_buttons), 0);
3339
3340 /* Create the buttons themselves */
3341 loader_button_options = gtk_button_new_with_label(_("Options..."));
3342 loader_button_import = gtk_button_new_with_label(_("Import"));
3343 loader_button_cancel = gtk_button_new_with_label(_("Cancel"));
3344 loader_button_about = gtk_button_new_with_label(_("About"));
3345
3346 /* Add actions to the buttons */
3347 g_signal_connect (G_OBJECT (loader_button_import), "clicked", G_CALLBACK (pgui_action_import), NULL);
3348 g_signal_connect (G_OBJECT (loader_button_options), "clicked", G_CALLBACK (pgui_action_loader_options_open), NULL);
3349 g_signal_connect (G_OBJECT (loader_button_cancel), "clicked", G_CALLBACK (pgui_action_cancel), NULL);
3350 g_signal_connect (G_OBJECT (loader_button_about), "clicked", G_CALLBACK (pgui_action_about_open), NULL);
3351
3352 /* And insert the buttons into the hbox */
3353 gtk_box_pack_start(GTK_BOX(loader_hbox_buttons), loader_button_options, TRUE, TRUE, 0);
3354 gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_cancel, TRUE, TRUE, 0);
3355 gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_about, TRUE, TRUE, 0);
3356 gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_import, TRUE, TRUE, 0);
3357
3358 /*
3359 ** Table selector
3360 */
3361 export_list_frame = gtk_frame_new(_("Export List"));
3362 pgui_create_export_table_table(export_list_frame);
3363
3364 /*
3365 ** Row of action buttons
3366 */
3367 dumper_hbox_buttons = gtk_hbox_new(TRUE, 15);
3368 gtk_container_set_border_width (GTK_CONTAINER (dumper_hbox_buttons), 0);
3369
3370 /* Create the buttons themselves */
3371 dumper_button_options = gtk_button_new_with_label(_("Options..."));
3372 dumper_button_export = gtk_button_new_with_label(_("Export"));
3373 dumper_button_cancel = gtk_button_new_with_label(_("Cancel"));
3374 dumper_button_about = gtk_button_new_with_label(_("About"));
3375
3376 /* Add actions to the buttons */
3377 g_signal_connect (G_OBJECT (dumper_button_export), "clicked", G_CALLBACK (pgui_action_export), NULL);
3378 g_signal_connect (G_OBJECT (dumper_button_options), "clicked", G_CALLBACK (pgui_action_dumper_options_open), NULL);
3379 g_signal_connect (G_OBJECT (dumper_button_cancel), "clicked", G_CALLBACK (pgui_action_cancel), NULL);
3380 g_signal_connect (G_OBJECT (dumper_button_about), "clicked", G_CALLBACK (pgui_action_about_open), NULL);
3381
3382 /* And insert the buttons into the hbox */
3383 gtk_box_pack_start(GTK_BOX(dumper_hbox_buttons), dumper_button_options, TRUE, TRUE, 0);
3384 gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_cancel, TRUE, TRUE, 0);
3385 gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_about, TRUE, TRUE, 0);
3386 gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_export, TRUE, TRUE, 0);
3387
3388 /*
3389 ** Log window
3390 */
3391 frame_log = gtk_frame_new(_("Log Window"));
3392 gtk_container_set_border_width (GTK_CONTAINER (frame_log), 0);
3393 gtk_widget_set_size_request(frame_log, -1, 200);
3394 textview_log = gtk_text_view_new();
3395 textbuffer_log = gtk_text_buffer_new(NULL);
3396 scrolledwindow_log = gtk_scrolled_window_new(NULL, NULL);
3397 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolledwindow_log), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3398 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview_log), textbuffer_log);
3399 gtk_container_set_border_width (GTK_CONTAINER (textview_log), 5);
3400 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview_log), FALSE);
3401 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview_log), FALSE);
3402 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview_log), GTK_WRAP_WORD);
3403 gtk_container_add (GTK_CONTAINER (scrolledwindow_log), textview_log);
3404 gtk_container_add (GTK_CONTAINER (frame_log), scrolledwindow_log);
3405
3406 /*
3407 ** Main window
3408 */
3409 vbox_main = gtk_vbox_new(FALSE, 10);
3410 gtk_container_set_border_width (GTK_CONTAINER (vbox_main), 0);
3411
3412 /* Add the loader frames into the notebook page */
3413 vbox_loader = gtk_vbox_new(FALSE, 10);
3414 gtk_container_set_border_width(GTK_CONTAINER(vbox_loader), 10);
3415
3416 gtk_box_pack_start(GTK_BOX(vbox_loader), import_list_frame, FALSE, TRUE, 0);
3417 gtk_box_pack_start(GTK_BOX(vbox_loader), loader_hbox_buttons, FALSE, FALSE, 0);
3418 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_loader, gtk_label_new(_("Import")));
3419
3420 /* Add the dumper frames into the notebook page */
3421 vbox_dumper = gtk_vbox_new(FALSE, 10);
3422 gtk_container_set_border_width(GTK_CONTAINER(vbox_dumper), 10);
3423
3424 gtk_box_pack_start(GTK_BOX(vbox_dumper), export_list_frame, FALSE, TRUE, 0);
3425 gtk_box_pack_start(GTK_BOX(vbox_dumper), dumper_hbox_buttons, FALSE, FALSE, 0);
3426 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_dumper, gtk_label_new(_("Export")));
3427
3428 /* Add the frames into the main vbox */
3429 gtk_box_pack_start(GTK_BOX(vbox_main), frame_pg, FALSE, TRUE, 0);
3430 gtk_box_pack_start(GTK_BOX(vbox_main), notebook, FALSE, TRUE, 0);
3431 gtk_box_pack_start(GTK_BOX(vbox_main), frame_log, TRUE, TRUE, 0);
3432
3433 /* and insert the vbox into the main window */
3434 gtk_container_add(GTK_CONTAINER(window_main), vbox_main);
3435
3436 /* make sure that everything, window and label, are visible */
3437 gtk_widget_show_all(window_main);
3438
3439 return;
3440 }
3441
3442 static void
usage()3443 usage()
3444 {
3445 printf("RCSID: %s RELEASE: %s\n", S2P_RCSID, POSTGIS_VERSION);
3446 printf("USAGE: shp2pgsql-gui [options]\n");
3447 printf("OPTIONS:\n");
3448 printf(" -U <username>\n");
3449 printf(" -W <password>\n");
3450 printf(" -h <host>\n");
3451 printf(" -p <port>\n");
3452 printf(" -d <database>\n");
3453 printf(" -? Display this help screen\n");
3454 }
3455
3456 int
main(int argc,char * argv[])3457 main(int argc, char *argv[])
3458 {
3459 int c;
3460
3461 #ifdef ENABLE_NLS
3462 setlocale (LC_ALL, "");
3463 bindtextdomain (PACKAGE, PGSQL_LOCALEDIR);
3464 textdomain (PACKAGE);
3465 #endif
3466
3467 /* Parse command line options and set configuration */
3468 global_loader_config = malloc(sizeof(SHPLOADERCONFIG));
3469 set_loader_config_defaults(global_loader_config);
3470 global_dumper_config = malloc(sizeof(SHPDUMPERCONFIG));
3471 set_dumper_config_defaults(global_dumper_config);
3472
3473 /* Here we override any defaults for the GUI */
3474 global_loader_config->createindex = 1;
3475 global_loader_config->geo_col = strdup(GEOMETRY_DEFAULT);
3476 global_loader_config->dump_format = 1;
3477
3478 conn = malloc(sizeof(SHPCONNECTIONCONFIG));
3479 memset(conn, 0, sizeof(SHPCONNECTIONCONFIG));
3480
3481 /* Here we override any defaults for the connection */
3482 conn->host = strdup("localhost");
3483 conn->port = strdup("5432");
3484
3485 while ((c = pgis_getopt(argc, argv, "U:p:W:d:h:")) != -1)
3486 {
3487 switch (c)
3488 {
3489 case 'U':
3490 conn->username = strdup(pgis_optarg);
3491 break;
3492 case 'p':
3493 conn->port = strdup(pgis_optarg);
3494 break;
3495 case 'W':
3496 conn->password = strdup(pgis_optarg);
3497 break;
3498 case 'd':
3499 conn->database = strdup(pgis_optarg);
3500 break;
3501 case 'h':
3502 conn->host = strdup(pgis_optarg);
3503 break;
3504 default:
3505 usage();
3506 free(conn);
3507 free(global_loader_config);
3508 exit(0);
3509 }
3510 }
3511
3512 /* initialize the GTK stack */
3513 gtk_init(&argc, &argv);
3514
3515 /* set up the user interface */
3516 pgui_create_main_window(conn);
3517 pgui_create_connection_window();
3518 pgui_create_loader_options_dialog();
3519 pgui_create_dumper_options_dialog();
3520 pgui_create_about_dialog();
3521 pgui_create_filechooser_dialog();
3522 pgui_create_progress_dialog();
3523 pgui_create_tablechooser_dialog();
3524 pgui_create_folderchooser_dialog();
3525
3526 /* start the main loop */
3527 gtk_main();
3528
3529 /* Free the configuration */
3530 free(conn);
3531 free(global_loader_config);
3532
3533 return 0;
3534 }
3535