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