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