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