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