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