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