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