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