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 
917  /* Copy the filename (we'll remove the .shp extension in a sec) */
918  loader_file_config->shp_file = strdup(filename);
919 
920  /* Generate the default table name from the filename */
921  table_start = loader_file_config->shp_file + strlen(loader_file_config->shp_file);
922  while (*table_start != '/' && *table_start != '\\' && table_start > loader_file_config->shp_file)
923  table_start--;
924 
925  /* Forward one to start of actual characters */
926  table_start++;
927 
928  /* Roll back from end to first . character. */
929  table_end = loader_file_config->shp_file + strlen(loader_file_config->shp_file);
930  while (*table_end != '.' && table_end > loader_file_config->shp_file && table_end > table_start )
931  table_end--;
932 
933  /* Copy the table name */
934  loader_file_config->table = malloc(table_end - table_start + 1);
935  memcpy(loader_file_config->table, table_start, table_end - table_start);
936  loader_file_config->table[table_end - table_start] = '\0';
937 
938  /* Force the table name to lower case */
939  for (i = 0; i < table_end - table_start; i++)
940  {
941  if (isupper(loader_file_config->table[i]) != 0)
942  loader_file_config->table[i] = tolower(loader_file_config->table[i]);
943  }
944 
945  /* Set the default schema to public */
946  loader_file_config->schema = strdup("public");
947 
948  /* Set the default geo column name */
950  loader_file_config->geo_col = strdup(GEOGRAPHY_DEFAULT);
951  else
952  loader_file_config->geo_col = strdup(GEOMETRY_DEFAULT);
953 
954  return loader_file_config;
955 }
956 
957 /* Given the loader configuration, add a new row representing this file to the listview */
958 static void
960 {
961  GtkTreeIter iter;
962 #define MAXLEN 16
963  char srid[MAXLEN+1];
964 
965  /* Convert SRID into string */
966  if ( MAXLEN+1 <= snprintf(srid, MAXLEN+1, "%d", loader_file_config->sr_id) )
967  {
968  pgui_logf("Invalid SRID requiring more than %d digits: %d", MAXLEN, loader_file_config->sr_id);
970  srid[MAXLEN] = '\0';
971  }
972 
973  gtk_list_store_insert_before(import_file_list_store, &iter, NULL);
974  gtk_list_store_set(import_file_list_store, &iter,
975  IMPORT_POINTER_COLUMN, loader_file_config,
976  IMPORT_FILENAME_COLUMN, loader_file_config->shp_file,
977  IMPORT_SCHEMA_COLUMN, loader_file_config->schema,
978  IMPORT_TABLE_COLUMN, loader_file_config->table,
979  IMPORT_GEOMETRY_COLUMN, loader_file_config->geo_col,
980  IMPORT_SRID_COLUMN, srid,
981  IMPORT_MODE_COLUMN, _("Create"),
982  -1);
983 
984  /* Update the filename field width */
986 
987  return;
988 }
989 
990 /* Free up the specified SHPLOADERCONFIG */
991 static void
993 {
994 
995  if (config->table)
996  free(config->table);
997 
998  if (config->schema)
999  free(config->schema);
1000 
1001  if (config->geo_col)
1002  free(config->geo_col);
1003 
1004  if (config->shp_file)
1005  free(config->shp_file);
1006 
1007  if (config->encoding)
1008  free(config->encoding);
1009 
1010  if (config->tablespace)
1011  free(config->tablespace);
1012 
1013  if (config->idxtablespace)
1014  free(config->idxtablespace);
1015 
1016  /* Free the config itself */
1017  free(config);
1018 }
1019 
1020 /* Given a table selection, generate a new configuration */
1021 static SHPDUMPERCONFIG *
1022 create_new_table_config(GtkTreeIter *iter)
1023 {
1024  SHPDUMPERCONFIG *dumper_table_config;
1025  gchar *schema, *table, *geocol;
1026  gint hasgeo;
1027 
1028  /* Generate a new configuration by copying the global options first and then
1029  adding in the specific values for this table */
1030  dumper_table_config = malloc(sizeof(SHPDUMPERCONFIG));
1031  memcpy(dumper_table_config, global_dumper_config, sizeof(SHPDUMPERCONFIG));
1032 
1033  /* Grab the values from the current iter */
1034  gtk_tree_model_get(GTK_TREE_MODEL(chooser_filtered_table_list_store), iter,
1035  TABLECHOOSER_SCHEMA_COLUMN, &schema,
1036  TABLECHOOSER_TABLE_COLUMN, &table,
1037  TABLECHOOSER_GEO_COLUMN, &geocol,
1038  TABLECHOOSER_HASGEO_COLUMN, &hasgeo,
1039  -1);
1040 
1041  /* Set up the values in the SHPDUMPERCONFIG */
1042  dumper_table_config->schema = strdup(schema);
1043  dumper_table_config->table = strdup(table);
1044 
1045  /* We also set the filename the same as the table name */
1046  dumper_table_config->shp_file = strdup(table);
1047 
1048  if (hasgeo && geocol)
1049  dumper_table_config->geo_col_name = strdup(geocol);
1050  else
1051  dumper_table_config->geo_col_name = NULL;
1052 
1053  return dumper_table_config;
1054 }
1055 
1056 /* Given the dumper configuration, add a new row representing this file to the listview. The liststore and iter arguments
1057 are optional, and enable the user to specify additional information to the view, e.g. geo column multi-choice. */
1058 static void
1059 add_dumper_table_config_to_list(SHPDUMPERCONFIG *dumper_table_config, GtkListStore *chooser_liststore, GtkTreeIter *chooser_iter)
1060 {
1061  GtkTreeIter iter;
1062  GtkListStore *geocol_liststore;
1063 
1064  gtk_list_store_insert_before(export_table_list_store, &iter, NULL);
1065  gtk_list_store_set(export_table_list_store, &iter,
1066  EXPORT_POINTER_COLUMN, dumper_table_config,
1067  EXPORT_SCHEMA_COLUMN, dumper_table_config->schema,
1068  EXPORT_TABLE_COLUMN, dumper_table_config->table,
1069  EXPORT_GEOMETRY_COLUMN, dumper_table_config->geo_col_name,
1070  EXPORT_FILENAME_COLUMN, dumper_table_config->shp_file,
1071  -1);
1072 
1073  /* If we have supplied the table_chooser store for additional information, use it */
1074  if (chooser_liststore)
1075  {
1076  /* Let's add a multi-choice geometry column to the table */
1077  gtk_tree_model_get(GTK_TREE_MODEL(chooser_liststore), chooser_iter,
1078  TABLECHOOSER_GEO_LISTSTORE_COLUMN, &geocol_liststore,
1079  -1);
1080 
1081  gtk_list_store_set(export_table_list_store, &iter,
1082  EXPORT_GEOMETRY_LISTSTORE_COLUMN, geocol_liststore,
1083  -1);
1084  }
1085 
1086  return;
1087 }
1088 
1089 /* Free up the specified SHPDUMPERCONFIG */
1090 static void
1092 {
1093 
1094  if (config->table)
1095  free(config->table);
1096 
1097  if (config->schema)
1098  free(config->schema);
1099 
1100  if (config->geo_col_name)
1101  free(config->geo_col_name);
1102 
1103  if (config->shp_file)
1104  free(config->shp_file);
1105 
1106  /* Free the config itself */
1107  free(config);
1108 }
1109 
1110 /* Validate a single DBF column type against a PostgreSQL type: return either TRUE or FALSE depending
1111  upon whether or not the type is (broadly) compatible */
1112 static int
1113 validate_shape_column_against_pg_column(int dbf_fieldtype, char *pg_fieldtype)
1114 {
1115  switch (dbf_fieldtype)
1116  {
1117  case FTString:
1118  /* Only varchar */
1119  if (!strcmp(pg_fieldtype, "varchar"))
1120  return -1;
1121  break;
1122 
1123  case FTDate:
1124  /* Only date */
1125  if (!strcmp(pg_fieldtype, "date"))
1126  return -1;
1127  break;
1128 
1129  case FTInteger:
1130  /* Tentatively allow int2, int4 and numeric */
1131  if (!strcmp(pg_fieldtype, "int2") || !strcmp(pg_fieldtype, "int4") || !strcmp(pg_fieldtype, "numeric"))
1132  return -1;
1133  break;
1134 
1135  case FTDouble:
1136  /* Only float8/numeric */
1137  if (!strcmp(pg_fieldtype, "float8") || !strcmp(pg_fieldtype, "numeric"))
1138  return -1;
1139  break;
1140 
1141  case FTLogical:
1142  /* Only boolean */
1143  if (!strcmp(pg_fieldtype, "boolean"))
1144  return -1;
1145  break;
1146  }
1147 
1148  /* Otherwise we can't guarantee this (but this is just a warning anyway) */
1149  return 0;
1150 }
1151 
1152 /* Validate column compatibility for the given loader configuration against the table/column
1153  list returned in result */
1154 static int
1156 {
1157  ExecStatusType status;
1158  SHPLOADERSTATE *state;
1159  int ntuples;
1160  char *pg_fieldname, *pg_fieldtype;
1161  int ret, i, j, found, response = SHPLOADEROK;
1162 
1163  /* Check the status of the result set */
1164  status = PQresultStatus(result);
1165  if (status == PGRES_TUPLES_OK)
1166  {
1167  ntuples = PQntuples(result);
1168 
1169  switch (config->opt)
1170  {
1171  case 'c':
1172  /* If we have a row matching the table given in the config, then it already exists */
1173  if (ntuples > 0)
1174  {
1175  pgui_seterr(_("ERROR: Create mode selected for existing table: %s.%s"), config->schema, config->table);
1176  response = SHPLOADERERR;
1177  }
1178  break;
1179 
1180  case 'p':
1181  /* If we have a row matching the table given in the config, then it already exists */
1182  if (ntuples > 0)
1183  {
1184  pgui_seterr(_("ERROR: Prepare mode selected for existing table: %s.%s"), config->schema, config->table);
1185  response = SHPLOADERERR;
1186  }
1187  break;
1188 
1189  case 'a':
1190  /* If we are trying to append to a table but it doesn't exist, emit a warning */
1191  if (ntuples == 0)
1192  {
1193  pgui_seterr(_("ERROR: Destination table %s.%s could not be found for appending"), config->schema, config->table);
1194  response = SHPLOADERERR;
1195  }
1196  else
1197  {
1198  /* If we have a row then lets do some simple column validation... */
1199  state = ShpLoaderCreate(config);
1200  ret = ShpLoaderOpenShape(state);
1201  if (ret != SHPLOADEROK)
1202  {
1203  pgui_logf(_("Warning: Could not load shapefile %s"), config->shp_file);
1204  ShpLoaderDestroy(state);
1205  }
1206 
1207  /* Find each column based upon its name and then validate type separately... */
1208  for (i = 0; i < state->num_fields; i++)
1209  {
1210  /* Make sure we find a column */
1211  found = 0;
1212  for (j = 0; j < ntuples; j++)
1213  {
1214  pg_fieldname = PQgetvalue(result, j, PQfnumber(result, "field"));
1215  pg_fieldtype = PQgetvalue(result, j, PQfnumber(result, "type"));
1216 
1217  if (!strcmp(state->field_names[i], pg_fieldname))
1218  {
1219  found = -1;
1220 
1221  ret = validate_shape_column_against_pg_column(state->types[i], pg_fieldtype);
1222  if (!ret)
1223  {
1224  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);
1225  response = SHPLOADERWARN;
1226  }
1227  }
1228  }
1229 
1230  /* Flag a warning if we can't find a match */
1231  if (!found)
1232  {
1233  pgui_logf(_("Warning: DBF Field '%s' within file %s could not be matched to a column within table %s.%s"),
1234  state->field_names[i], config->shp_file, config->schema, config->table);
1235  response = SHPLOADERWARN;
1236  }
1237  }
1238 
1239  ShpLoaderDestroy(state);
1240  }
1241 
1242  break;
1243  }
1244  }
1245  else
1246  {
1247  pgui_seterr(_("ERROR: unable to process validation response from remote server"));
1248  response = SHPLOADERERR;
1249  }
1250 
1251  return response;
1252 }
1253 
1254 /* Terminate the main loop and exit the application. */
1255 static void
1256 pgui_quit (GtkWidget *widget, gpointer data)
1257 {
1258  gtk_main_quit();
1259 }
1260 
1261 static void
1263 {
1264  /* Display the dialog and hide it again upon exit */
1265  gtk_dialog_run(GTK_DIALOG(dialog_about));
1266  gtk_widget_hide(dialog_about);
1267 }
1268 
1269 static void
1270 pgui_action_cancel(GtkWidget *widget, gpointer data)
1271 {
1272  if (!is_running)
1273  pgui_quit(widget, data); /* quit if we're not running */
1274  else
1275  is_running = FALSE;
1276 }
1277 
1278 static void
1279 pgui_action_loader_options_open(GtkWidget *widget, gpointer data)
1280 {
1282  gtk_widget_show_all(dialog_loader_options);
1283  return;
1284 }
1285 
1286 static void
1287 pgui_action_loader_options_close(GtkWidget *widget, gint response, gpointer data)
1288 {
1289  /* Only update the configuration if the user hit OK */
1290  if (response == GTK_RESPONSE_OK)
1292 
1293  /* Hide the dialog */
1294  gtk_widget_hide(dialog_loader_options);
1295 
1296  return;
1297 }
1298 
1299 static void
1300 pgui_action_open_file_dialog(GtkWidget *widget, gpointer data)
1301 {
1302  SHPLOADERCONFIG *loader_file_config;
1303  GSList *filename_list, *filename_item;
1304  gchar *filename;
1305 
1306  /* Make sure we deselect any files from the last time */
1307  gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog_filechooser));
1308 
1309  /* Run the dialog */
1310  if (gtk_dialog_run(GTK_DIALOG(dialog_filechooser)) == GTK_RESPONSE_ACCEPT)
1311  {
1312  /* Create the new file configuration based upon the each filename and add it to the listview */
1313  filename_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog_filechooser));
1314 
1315  filename_item = g_slist_nth(filename_list, 0);
1316  while (filename_item)
1317  {
1318  /* Add the configuration */
1319  filename = g_slist_nth_data(filename_item, 0);
1320 
1321  loader_file_config = create_new_file_config(filename);
1322  add_loader_file_config_to_list(loader_file_config);
1323 
1324  /* Grab the next filename */
1325  filename_item = g_slist_next(filename_item);
1326  }
1327 
1328  /* Free the list */
1329  g_slist_free(filename_list);
1330  }
1331 
1332  gtk_widget_hide(dialog_filechooser);
1333 }
1334 
1335 static void
1336 pgui_action_open_table_dialog(GtkWidget *widget, gpointer data)
1337 {
1338  SHPDUMPERCONFIG *dumper_table_config;
1339  GtkTreeSelection *chooser_selection;
1340  GtkTreeModel *model;
1341  GList *selected_rows_list, *selected_row;
1342  GtkTreeIter iter;
1343  GtkTreePath *tree_path;
1344 
1345  /* Make sure we can connect to the database first */
1346  if (!connection_test())
1347  {
1348  pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
1350 
1351  /* Open the connections UI for the user */
1353 
1354  gtk_widget_show_all(GTK_WIDGET(window_conn));
1355  return;
1356  }
1357 
1358  /* Setup the form */
1360  gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(chooser_filtered_table_list_store));
1361 
1362  /* Run the dialog */
1363  gtk_widget_show_all(dialog_tablechooser);
1364  if (gtk_dialog_run(GTK_DIALOG(dialog_tablechooser)) == GTK_RESPONSE_OK)
1365  {
1366  /* Create the new dumper configuration based upon the selected iters and add them to the listview */
1367  chooser_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(chooser_tree));
1368 
1369  selected_rows_list = gtk_tree_selection_get_selected_rows(chooser_selection, &model);
1370  selected_row = g_list_first(selected_rows_list);
1371  while (selected_row)
1372  {
1373  /* Get the tree iter */
1374  tree_path = (GtkTreePath *)g_list_nth_data(selected_row, 0);
1375  gtk_tree_model_get_iter(model, &iter, tree_path);
1376 
1377  /* Get the config and add it to the list */
1378  dumper_table_config = create_new_table_config(&iter);
1380 
1381  /* Get the next row */
1382  selected_row = g_list_next(selected_row);
1383  }
1384 
1385  /* Free the GList */
1386  g_list_foreach(selected_row, (GFunc)gtk_tree_path_free, NULL);
1387  g_list_free(selected_row);
1388  }
1389 
1390  gtk_widget_hide(dialog_tablechooser);
1391 }
1392 
1393 /*
1394  * Signal handler for the remove box. Performs no user interaction, simply
1395  * removes the row from the table.
1396  */
1397 static void
1398 pgui_action_handle_table_remove(GtkCellRendererToggle *renderer,
1399  gchar *path,
1400  gpointer user_data)
1401 {
1402  GtkTreeIter iter;
1403  SHPDUMPERCONFIG *dumper_table_config;
1404  gpointer gptr;
1405 
1406  /* Grab the SHPDUMPERCONFIG from the EXPORT_POINTER_COLUMN for the list store */
1407  gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path);
1408  gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
1409  dumper_table_config = (SHPDUMPERCONFIG *)gptr;
1410 
1411  /* Free the configuration from memory */
1412  free_dumper_config(dumper_table_config);
1413 
1414  /* Remove the row from the list */
1415  gtk_list_store_remove(export_table_list_store, &iter);
1416 }
1417 
1418 static void
1419 pgui_action_import(GtkWidget *widget, gpointer data)
1420 {
1421  SHPLOADERCONFIG *loader_file_config;
1422  SHPLOADERSTATE *state;
1423  gint is_valid;
1424  gpointer gptr;
1425  GtkTreeIter iter;
1426  char *sql_form, *query, *connection_string, *progress_shapefile = NULL;
1427  char progress_text[GUIMSG_LINE_MAXLEN+1];
1428  PGresult *result;
1429 
1430  int ret, i = 0;
1431  char *header, *footer, *record;
1432 
1433  /* Get the first row of the import list */
1434  is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
1435  if (!is_valid)
1436  {
1437  pgui_seterr(_("ERROR: You haven't specified any files to import"));
1439 
1440  return;
1441  }
1442 
1443  /* Firstly make sure that we can connect to the database - if we can't then there isn't much
1444  point doing anything else... */
1445  if (!connection_test())
1446  {
1447  pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
1449 
1450  /* Open the connections UI for the user */
1452 
1453  gtk_widget_show_all(GTK_WIDGET(window_conn));
1454  return;
1455  }
1456 
1457  /* Let's open a single connection to the remote DB for the duration of the validation pass;
1458  note that we already know the connection string works, otherwise we would have bailed
1459  out earlier in the function */
1460  connection_string = ShpDumperGetConnectionStringFromConn(conn);
1461  pg_connection = PQconnectdb(connection_string);
1462 
1463  /* Setup the table/column type discovery query */
1464  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";
1465 
1466  /* Validation: we loop through each of the files in order to validate them as a separate pass */
1467  while (is_valid)
1468  {
1469  size_t sz;
1470  /* Grab the SHPLOADERCONFIG for this row */
1471  gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
1472  loader_file_config = (SHPLOADERCONFIG *)gptr;
1473 
1474  /* For each entry, we execute a remote query in order to determine the column names
1475  and types for the remote table if they actually exist */
1476  sz = strlen(sql_form) + strlen(loader_file_config->schema) + strlen(loader_file_config->table) + 1;
1477  query = malloc(sz);
1478  snprintf(query, sz, sql_form, loader_file_config->table, loader_file_config->schema);
1479  result = PQexec(pg_connection, query);
1480 
1481  /* Call the validation function with the SHPLOADERCONFIG and the result set */
1482  ret = validate_remote_loader_columns(loader_file_config, result);
1483  if (ret == SHPLOADERERR)
1484  {
1486 
1487  PQclear(result);
1488  free(query);
1489 
1490  return;
1491  }
1492 
1493  /* Free the SQL query */
1494  PQclear(result);
1495  free(query);
1496 
1497  /* Get next entry */
1498  is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
1499  }
1500 
1501  /* Close our database connection */
1502  PQfinish(pg_connection);
1503 
1504 
1505  /* Once we've done the validation pass, now let's load the shapefile */
1506  is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
1507  while (is_valid)
1508  {
1509  /* Grab the SHPLOADERCONFIG for this row */
1510  gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
1511  loader_file_config = (SHPLOADERCONFIG *)gptr;
1512 
1513  pgui_logf("\n==============================");
1514  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);
1515 
1516  /*
1517  * Loop through the items in the shapefile
1518  */
1519  is_running = TRUE;
1520 
1521  /* One connection per file, otherwise error handling becomes tricky... */
1522  pg_connection = PQconnectdb(connection_string);
1523 
1524  /* Disable the button to prevent multiple imports running at the same time */
1525  gtk_widget_set_sensitive(widget, FALSE);
1526 
1527  /* Allow GTK events to get a look in */
1528  while (gtk_events_pending())
1529  gtk_main_iteration();
1530 
1531  /* Create the shapefile state object */
1532  state = ShpLoaderCreate(loader_file_config);
1533 
1534  /* Open the shapefile */
1535  ret = ShpLoaderOpenShape(state);
1536  if (ret != SHPLOADEROK)
1537  {
1538  pgui_logf("%s", state->message);
1539 
1540  if (ret == SHPLOADERERR)
1541  goto import_cleanup;
1542  }
1543 
1544  /* For progress display, only show the "core" filename */
1545  for (i = strlen(loader_file_config->shp_file); i >= 0
1546  && loader_file_config->shp_file[i - 1] != '\\' && loader_file_config->shp_file[i - 1] != '/'; i--);
1547 
1548  progress_shapefile = malloc(strlen(loader_file_config->shp_file));
1549  strcpy(progress_shapefile, &loader_file_config->shp_file[i]);
1550 
1551  /* Display the progress dialog */
1552  snprintf(progress_text, GUIMSG_LINE_MAXLEN, _("Importing shapefile %s (%d records)..."), progress_shapefile, ShpLoaderGetRecordCount(state));
1553  progress_text[GUIMSG_LINE_MAXLEN] = '\0';
1554  gtk_label_set_text(GTK_LABEL(label_progress), progress_text);
1555  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.0);
1556  gtk_widget_show_all(dialog_progress);
1557 
1558  /* If reading the whole shapefile, display its type */
1559  if (state->config->readshape)
1560  {
1561  pgui_logf("Shapefile type: %s", SHPTypeName(state->shpfiletype));
1562  pgui_logf("PostGIS type: %s[%d]", state->pgtype, state->pgdims);
1563  }
1564 
1565  /* Get the header */
1566  ret = ShpLoaderGetSQLHeader(state, &header);
1567  if (ret != SHPLOADEROK)
1568  {
1569  pgui_logf("%s", state->message);
1570 
1571  if (ret == SHPLOADERERR)
1572  goto import_cleanup;
1573  }
1574 
1575  /* Send the header to the remote server: if we are in COPY mode then the last
1576  statement will be a COPY and so will change connection mode */
1577  ret = pgui_exec(header);
1578  free(header);
1579 
1580  if (!ret)
1581  goto import_cleanup;
1582 
1583  /* If we are in prepare mode, we need to skip the actual load. */
1584  if (state->config->opt != 'p')
1585  {
1586  int numrecords = ShpLoaderGetRecordCount(state);
1587  int records_per_tick = (numrecords / 200) - 1;
1588 
1589  if ( records_per_tick < 1 )
1590  records_per_tick = 1;
1591 
1592  /* If we are in COPY (dump format) mode, output the COPY statement and enter COPY mode */
1593  if (state->config->dump_format)
1594  {
1595  ret = ShpLoaderGetSQLCopyStatement(state, &header);
1596 
1597  if (ret != SHPLOADEROK)
1598  {
1599  pgui_logf("%s", state->message);
1600 
1601  if (ret == SHPLOADERERR)
1602  goto import_cleanup;
1603  }
1604 
1605  /* Send the result to the remote server: this should put us in COPY mode */
1606  ret = pgui_copy_start(header);
1607  free(header);
1608 
1609  if (!ret)
1610  goto import_cleanup;
1611  }
1612 
1613  /* Main loop: iterate through all of the records and send them to stdout */
1614  for (i = 0; i < numrecords && is_running; i++)
1615  {
1616  ret = ShpLoaderGenerateSQLRowStatement(state, i, &record);
1617 
1618  switch (ret)
1619  {
1620  case SHPLOADEROK:
1621  /* Simply send the statement */
1622  if (state->config->dump_format)
1623  ret = pgui_copy_write(record);
1624  else
1625  ret = pgui_exec(record);
1626 
1627  /* Display a record number if we failed */
1628  if (!ret)
1629  pgui_logf(_("Import failed on record number %d"), i);
1630 
1631  free(record);
1632  break;
1633 
1634  case SHPLOADERERR:
1635  /* Display the error message then stop */
1636  pgui_logf("%s\n", state->message);
1637  goto import_cleanup;
1638  break;
1639 
1640  case SHPLOADERWARN:
1641  /* Display the warning, but continue */
1642  pgui_logf("%s\n", state->message);
1643 
1644  if (state->config->dump_format)
1645  ret = pgui_copy_write(record);
1646  else
1647  ret = pgui_exec(record);
1648 
1649  /* Display a record number if we failed */
1650  if (!ret)
1651  pgui_logf(_("Import failed on record number %d"), i);
1652 
1653  free(record);
1654  break;
1655 
1656  case SHPLOADERRECDELETED:
1657  /* Record is marked as deleted - ignore */
1658  break;
1659 
1660  case SHPLOADERRECISNULL:
1661  /* Record is NULL and should be ignored according to NULL policy */
1662  break;
1663  }
1664 
1665  /* Update the progress bar */
1666  if ( i % records_per_tick == 0 )
1667  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), (float)i / numrecords);
1668 
1669  /* Allow GTK events to get a look in */
1670  while (gtk_events_pending())
1671  gtk_main_iteration();
1672  }
1673 
1674  /* If we are in COPY (dump format) mode, leave COPY mode */
1675  if (state->config->dump_format)
1676  {
1677  if (! pgui_copy_end(0) )
1678  goto import_cleanup;
1679 
1680  result = PQgetResult(pg_connection);
1681  if (PQresultStatus(result) != PGRES_COMMAND_OK)
1682  {
1683  pgui_logf(_("COPY failed with the following error: %s"), PQerrorMessage(pg_connection));
1684  ret = SHPLOADERERR;
1685  goto import_cleanup;
1686  }
1687  }
1688  } /* if (state->config->opt != 'p') */
1689 
1690  /* Only continue if we didn't abort part way through */
1691  if (is_running)
1692  {
1693  /* Get the footer */
1694  ret = ShpLoaderGetSQLFooter(state, &footer);
1695  if (ret != SHPLOADEROK)
1696  {
1697  pgui_logf("%s\n", state->message);
1698 
1699  if (ret == SHPLOADERERR)
1700  goto import_cleanup;
1701  }
1702 
1703  /* Just in case index creation takes a long time, update the progress text */
1704  if (state->config->createindex)
1705  {
1706  gtk_label_set_text(GTK_LABEL(label_progress), _("Creating spatial index..."));
1707 
1708  /* Allow GTK events to get a look in */
1709  while (gtk_events_pending())
1710  gtk_main_iteration();
1711  }
1712 
1713  /* Send the footer to the server */
1714  ret = pgui_exec(footer);
1715  free(footer);
1716 
1717  if (!ret)
1718  goto import_cleanup;
1719  }
1720 
1721 import_cleanup:
1722  /* Import has definitely stopped running */
1723  is_running = FALSE;
1724 
1725  /* Close the existing connection */
1726  PQfinish(pg_connection);
1727  pg_connection = NULL;
1728 
1729  /* If we didn't finish inserting all of the items (and we expected to), an error occurred */
1730  if ((state->config->opt != 'p' && i != ShpLoaderGetRecordCount(state)) || !ret)
1731  pgui_logf(_("Shapefile import failed."));
1732  else
1733  pgui_logf(_("Shapefile import completed."));
1734 
1735  /* Free the state object */
1736  ShpLoaderDestroy(state);
1737 
1738  /* Tidy up */
1739  if (progress_shapefile)
1740  free(progress_shapefile);
1741 
1742  /* Get next entry */
1743  is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
1744  }
1745 
1746  /* Import has definitely finished */
1747  is_running = FALSE;
1748 
1749  /* Enable the button once again */
1750  gtk_widget_set_sensitive(widget, TRUE);
1751 
1752  /* Silly GTK bug means we have to hide and show the button for it to work again! */
1753  gtk_widget_hide(widget);
1754  gtk_widget_show(widget);
1755 
1756  /* Hide the progress dialog */
1757  gtk_widget_hide(dialog_progress);
1758 
1759  /* Allow GTK events to get a look in */
1760  while (gtk_events_pending())
1761  gtk_main_iteration();
1762 
1763  /* Tidy up */
1764  free(connection_string);
1765 
1766  return;
1767 }
1768 
1769 static void
1770 pgui_action_export(GtkWidget *widget, gpointer data)
1771 {
1772  SHPDUMPERCONFIG *dumper_table_config;
1773  SHPDUMPERSTATE *state;
1774  gint is_valid;
1775  gpointer gptr;
1776  GtkTreeIter iter;
1777  char *output_shapefile, *orig_shapefile;
1778  char progress_text[GUIMSG_LINE_MAXLEN+1];
1779  gchar *folder_path;
1780 
1781  int ret, success = FALSE, i = 0;
1782 
1783  /* Get the first row of the import list */
1784  is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(export_table_list_store), &iter);
1785  if (!is_valid)
1786  {
1787  pgui_seterr(_("ERROR: You haven't specified any tables to export"));
1789 
1790  return;
1791  }
1792 
1793  /* Firstly make sure that we can connect to the database - if we can't then there isn't much
1794  point doing anything else... */
1795  if (!connection_test())
1796  {
1797  pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
1799 
1800  /* Open the connections UI for the user */
1802 
1803  gtk_widget_show_all(GTK_WIDGET(window_conn));
1804  return;
1805  }
1806 
1807  /* Now open the file selector dialog so the user can specify where they would like the output
1808  files to reside */
1809  if (gtk_dialog_run(GTK_DIALOG(dialog_folderchooser)) != GTK_RESPONSE_ACCEPT)
1810  {
1811  gtk_widget_hide(dialog_folderchooser);
1812 
1813  return;
1814  }
1815 
1816  gtk_widget_hide(dialog_folderchooser);
1817  folder_path = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog_folderchooser));
1818 
1819  /* Now everything is set up, let's extract the tables */
1820  is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(export_table_list_store), &iter);
1821  while (is_valid)
1822  {
1823  /* Grab the SHPDUMPERCONFIG for this row */
1824  gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
1825  dumper_table_config = (SHPDUMPERCONFIG *)gptr;
1826 
1827  pgui_logf("\n==============================");
1828  pgui_logf("Exporting with configuration: %s, %s, %s", dumper_table_config->table, dumper_table_config->schema, dumper_table_config->shp_file);
1829 
1830  /* Export is running */
1831  is_running = TRUE;
1832  success = FALSE;
1833 
1834  /* Disable the button to prevent multiple imports running at the same time */
1835  gtk_widget_set_sensitive(widget, FALSE);
1836 
1837  /* Allow GTK events to get a look in */
1838  while (gtk_events_pending())
1839  gtk_main_iteration();
1840 
1841  /* Create the state for each configuration */
1842  state = ShpDumperCreate(dumper_table_config);
1843  state->config->conn = conn;
1844 
1845  /* Save the original shapefile name, then create a temporary version containing the full path */
1846  orig_shapefile = dumper_table_config->shp_file;
1847  output_shapefile = malloc(strlen(folder_path) + strlen(dumper_table_config->shp_file) + 2);
1848  strcpy(output_shapefile, folder_path);
1849  strcat(output_shapefile, G_DIR_SEPARATOR_S);
1850  strcat(output_shapefile, dumper_table_config->shp_file);
1851 
1852  dumper_table_config->shp_file = output_shapefile;
1853 
1854  /* Connect to the database */
1855  ret = ShpDumperConnectDatabase(state);
1856  if (ret != SHPDUMPEROK)
1857  {
1858  pgui_seterr("%s", state->message);
1860 
1861  goto export_cleanup;
1862  }
1863 
1864  /* Display the progress dialog */
1865  gtk_label_set_text(GTK_LABEL(label_progress), _("Initialising..."));
1866  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.0);
1867  gtk_widget_show_all(dialog_progress);
1868 
1869  ret = ShpDumperOpenTable(state);
1870  if (ret != SHPDUMPEROK)
1871  {
1872  pgui_logf("%s", state->message);
1873 
1874  if (ret == SHPDUMPERERR)
1875  {
1876  gtk_widget_hide(dialog_progress);
1877 
1878  pgui_seterr("%s", state->message);
1880 
1881  goto export_cleanup;
1882  }
1883  }
1884 
1885  /* Update the text */
1886  snprintf(progress_text, GUIMSG_LINE_MAXLEN, _("Exporting table %s (%d records)..."), dumper_table_config->table, ShpDumperGetRecordCount(state));
1887  progress_text[GUIMSG_LINE_MAXLEN] = '\0';
1888  gtk_label_set_text(GTK_LABEL(label_progress), progress_text);
1889 
1890  /* Allow GTK events to get a look in */
1891  while (gtk_events_pending())
1892  gtk_main_iteration();
1893 
1894  pgui_logf(_("Done (postgis major version: %d)"), state->pgis_major_version);
1895  pgui_logf(_("Output shape: %s"), shapetypename(state->outshptype));
1896 
1897  for (i = 0; i < ShpDumperGetRecordCount(state) && is_running == TRUE; i++)
1898  {
1899  ret = ShpLoaderGenerateShapeRow(state);
1900  if (ret != SHPDUMPEROK)
1901  {
1902  pgui_logf("%s", state->message);
1903 
1904  if (ret == SHPDUMPERERR)
1905  {
1906  gtk_widget_hide(dialog_progress);
1907 
1908  pgui_seterr("%s", state->message);
1910 
1911  goto export_cleanup;
1912  }
1913  }
1914 
1915  /* Update the progress bar */
1916  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), (float)i / ShpDumperGetRecordCount(state));
1917 
1918  /* Allow GTK events to get a look in */
1919  while (gtk_events_pending())
1920  gtk_main_iteration();
1921  }
1922 
1923  /* Finish the dump */
1924  ret = ShpDumperCloseTable(state);
1925  if (ret != SHPDUMPEROK)
1926  {
1927  pgui_logf("%s", state->message);
1928 
1929  if (ret == SHPDUMPERERR)
1930  {
1931  gtk_widget_hide(dialog_progress);
1932 
1933  pgui_seterr("%s", state->message);
1935  }
1936  }
1937 
1938  /* Indicate success */
1939  if (is_running)
1940  success = TRUE;
1941 
1942 export_cleanup:
1943 
1944  /* Tidy up everything */
1945  ShpDumperDestroy(state);
1946 
1947  /* Reset shapefile back to original form (without full path) */
1948  dumper_table_config->shp_file = orig_shapefile;
1949 
1950  /* Get next entry */
1951  is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(export_table_list_store), &iter);
1952  }
1953 
1954  /* Export has definitely finished */
1955  is_running = FALSE;
1956  if (!success)
1957  pgui_logf(_("Table export failed."));
1958  else
1959  pgui_logf(_("Table export completed."));
1960 
1961  /* Enable the button once again */
1962  gtk_widget_set_sensitive(widget, TRUE);
1963 
1964  /* Silly GTK bug means we have to hide and show the button for it to work again! */
1965  gtk_widget_hide(widget);
1966  gtk_widget_show(widget);
1967 
1968  /* Hide the progress dialog */
1969  gtk_widget_hide(dialog_progress);
1970 
1971  /* Allow GTK events to get a look in */
1972  while (gtk_events_pending())
1973  gtk_main_iteration();
1974 
1975  return;
1976 }
1977 
1978 
1979 /* === Import ListView functions and signal handlers === */
1980 
1981 /* Creates a single file row in the list table given the URI of a file */
1982 static void
1984 {
1985  SHPLOADERCONFIG *loader_file_config;
1986  char *filename = NULL;
1987  char *hostname;
1988  GError *error = NULL;
1989 
1990  if (uri == NULL)
1991  {
1992  pgui_logf(_("Unable to process drag URI."));
1993  return;
1994  }
1995 
1996  filename = g_filename_from_uri(uri, &hostname, &error);
1997  g_free(uri);
1998 
1999  if (filename == NULL)
2000  {
2001  pgui_logf(_("Unable to process filename: %s\n"), error->message);
2002  g_error_free(error);
2003  return;
2004  }
2005 
2006  /* Create a new row in the listview */
2007  loader_file_config = create_new_file_config(filename);
2008  add_loader_file_config_to_list(loader_file_config);
2009 
2010  g_free(filename);
2011  g_free(hostname);
2012 
2013 }
2014 
2015 /* Update the SHPLOADERCONFIG to the values currently contained within the iter */
2016 static void
2017 update_loader_file_config_from_listview_iter(GtkTreeIter *iter, SHPLOADERCONFIG *loader_file_config)
2018 {
2019  gchar *schema, *table, *geo_col, *srid;
2020 
2021  /* Grab the main values for this file */
2022  gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), iter,
2023  IMPORT_SCHEMA_COLUMN, &schema,
2024  IMPORT_TABLE_COLUMN, &table,
2025  IMPORT_GEOMETRY_COLUMN, &geo_col,
2026  IMPORT_SRID_COLUMN, &srid,
2027  -1);
2028 
2029  /* Update the schema */
2030  if (loader_file_config->schema)
2031  free(loader_file_config->schema);
2032 
2033  loader_file_config->schema = strdup(schema);
2034 
2035  /* Update the table */
2036  if (loader_file_config->table)
2037  free(loader_file_config->table);
2038 
2039  loader_file_config->table = strdup(table);
2040 
2041  /* Update the geo column */
2042  if (loader_file_config->geo_col)
2043  free(loader_file_config->geo_col);
2044 
2045  loader_file_config->geo_col = strdup(geo_col);
2046 
2047  /* Update the SRID */
2048  loader_file_config->sr_id = atoi(srid);
2049 
2050  /* Free the values */
2051  return;
2052 }
2053 
2054 
2055 /*
2056  * Here lives the magic of the drag-n-drop of the app. We really don't care
2057  * about much of the provided tidbits. We only actually user selection_data
2058  * and extract a list of filenames from it.
2059  */
2060 static void
2062  GdkDragContext *dc,
2063  gint x, gint y,
2064  GtkSelectionData *selection_data,
2065  guint info, guint t, gpointer data)
2066 {
2067  const gchar *p, *q;
2068 
2069  if (selection_data->data == NULL)
2070  {
2071  pgui_logf(_("Unable to process drag data."));
2072  return;
2073  }
2074 
2075  p = (char*)selection_data->data;
2076  while (p)
2077  {
2078  /* Only process non-comments */
2079  if (*p != '#')
2080  {
2081  /* Trim leading whitespace */
2082  while (g_ascii_isspace(*p))
2083  p++;
2084  q = p;
2085  /* Scan to the end of the string (null or newline) */
2086  while (*q && (*q != '\n') && (*q != '\r'))
2087  q++;
2088  if (q > p)
2089  {
2090  /* Ignore terminating character */
2091  q--;
2092  /* Trim trailing whitespace */
2093  while (q > p && g_ascii_isspace(*q))
2094  q--;
2095  if (q > p)
2096  {
2097  process_single_uri(g_strndup(p, q - p + 1));
2098  }
2099  }
2100  }
2101  /* Skip to the next entry */
2102  p = strchr(p, '\n');
2103  if (p)
2104  p++;
2105  }
2106 }
2107 
2108 
2109 /*
2110  * This function is a signal handler for the load mode combo boxes.
2111  */
2112 static void
2113 pgui_action_handle_tree_combo(GtkCellRendererCombo *combo,
2114  gchar *path_string,
2115  GtkTreeIter *new_iter,
2116  gpointer user_data)
2117 {
2118  GtkTreeIter iter;
2119  SHPLOADERCONFIG *loader_file_config;
2120  char opt;
2121  gchar *combo_text;
2122  gpointer gptr;
2123 
2124  /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
2125  gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path_string);
2126  gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
2127  loader_file_config = (SHPLOADERCONFIG *)gptr;
2128 
2129  /* Now grab the row selected within the combo box */
2130  gtk_tree_model_get(GTK_TREE_MODEL(loader_mode_combo_list), new_iter, LOADER_MODE_COMBO_OPTION_CHAR, &opt, -1);
2131 
2132  /* Update the configuration */
2133 
2134  /* Hack for index creation: we must disable it if we are appending, otherwise we
2135  end up trying to generate the index again */
2136  loader_file_config->createindex = global_loader_config->createindex;
2137 
2138  switch (opt)
2139  {
2140  case 'a':
2141  loader_file_config->opt = 'a';
2142 
2143  /* Other half of index creation hack */
2144  loader_file_config->createindex = 0;
2145 
2146  break;
2147 
2148  case 'd':
2149  loader_file_config->opt = 'd';
2150  break;
2151 
2152  case 'p':
2153  loader_file_config->opt = 'p';
2154  break;
2155 
2156  case 'c':
2157  loader_file_config->opt = 'c';
2158  break;
2159  }
2160 
2161  /* Update the selection in the listview with the text from the combo */
2162  gtk_tree_model_get(GTK_TREE_MODEL(loader_mode_combo_list), new_iter, LOADER_MODE_COMBO_TEXT, &combo_text, -1);
2163  gtk_list_store_set(import_file_list_store, &iter, IMPORT_MODE_COLUMN, combo_text, -1);
2164 
2165  return;
2166 }
2167 
2168 
2169 /*
2170  * This method is a signal listener for all text renderers in the file
2171  * list table, including the empty ones. Edits of the empty table are
2172  * passed to an appropriate function, while edits of existing file rows
2173  * are applied and the various validations called.
2174  */
2175 static void
2176 pgui_action_handle_loader_edit(GtkCellRendererText *renderer,
2177  gchar *path,
2178  gchar *new_text,
2179  gpointer column)
2180 {
2181  GtkTreeIter iter;
2182  gpointer gptr;
2183  gint columnindex;
2184  SHPLOADERCONFIG *loader_file_config;
2185 #define MAXLEN 16
2186  char srid[MAXLEN+1];
2187 
2188  /* Empty doesn't fly */
2189  if (strlen(new_text) == 0)
2190  return;
2191 
2192  /* Update the model with the current edit change */
2193  columnindex = *(gint *)column;
2194  gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path);
2195  gtk_list_store_set(import_file_list_store, &iter, columnindex, new_text, -1);
2196 
2197  /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
2198  gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
2199  loader_file_config = (SHPLOADERCONFIG *)gptr;
2200 
2201  /* Update the configuration from the current UI data */
2202  update_loader_file_config_from_listview_iter(&iter, loader_file_config);
2203 
2204  /* Now refresh the listview UI row with the new configuration */
2205  if ( MAXLEN+1 <= snprintf(srid, MAXLEN+1, "%d", loader_file_config->sr_id) )
2206  {
2207  pgui_logf("Invalid SRID requiring more than %d digits: %d", MAXLEN, loader_file_config->sr_id);
2209  srid[MAXLEN] = '\0';
2210  }
2211 
2212  gtk_list_store_set(import_file_list_store, &iter,
2213  IMPORT_SCHEMA_COLUMN, loader_file_config->schema,
2214  IMPORT_TABLE_COLUMN, loader_file_config->table,
2215  IMPORT_GEOMETRY_COLUMN, loader_file_config->geo_col,
2216  IMPORT_SRID_COLUMN, srid,
2217  -1);
2218 
2219  return;
2220 }
2221 
2222 /*
2223  * Signal handler for the remove box. Performs no user interaction, simply
2224  * removes the row from the table.
2225  */
2226 static void
2227 pgui_action_handle_file_remove(GtkCellRendererToggle *renderer,
2228  gchar *path,
2229  gpointer user_data)
2230 {
2231  GtkTreeIter iter;
2232  SHPLOADERCONFIG *loader_file_config;
2233  gpointer gptr;
2234 
2235  /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
2236  gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path);
2237  gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
2238  loader_file_config = (SHPLOADERCONFIG *)gptr;
2239 
2240  /* Free the configuration from memory */
2241  free_loader_config(loader_file_config);
2242 
2243  /* Remove the row from the list */
2244  gtk_list_store_remove(import_file_list_store, &iter);
2245 
2246  /* Update the filename field width */
2248 }
2249 
2250 
2251 /* === Export ListView functions and signal handlers === */
2252 
2253 /* Update the SHPDUMPERCONFIG to the values currently contained within the iter */
2254 static void
2255 update_dumper_table_config_from_listview_iter(GtkTreeIter *iter, SHPDUMPERCONFIG *dumper_table_config)
2256 {
2257  gchar *schema, *table, *geo_col, *filename;
2258 
2259  /* Grab the main values for this file */
2260  gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), iter,
2261  EXPORT_SCHEMA_COLUMN, &schema,
2262  EXPORT_TABLE_COLUMN, &table,
2263  EXPORT_GEOMETRY_COLUMN, &geo_col,
2264  EXPORT_FILENAME_COLUMN, &filename,
2265  -1);
2266 
2267  /* Update the schema */
2268  if (dumper_table_config->schema)
2269  free(dumper_table_config->schema);
2270 
2271  dumper_table_config->schema = strdup(schema);
2272 
2273  /* Update the table */
2274  if (dumper_table_config->table)
2275  free(dumper_table_config->table);
2276 
2277  dumper_table_config->table = strdup(table);
2278 
2279  /* Update the geometry column */
2280  if (dumper_table_config->geo_col_name)
2281  free(dumper_table_config->geo_col_name);
2282 
2283  dumper_table_config->geo_col_name = strdup(geo_col);
2284 
2285  /* Update the filename column (default to table name) */
2286  if (dumper_table_config->shp_file)
2287  free(dumper_table_config->shp_file);
2288 
2289  dumper_table_config->shp_file = strdup(filename);
2290 
2291  return;
2292 }
2293 
2294 static void
2295 pgui_action_handle_table_geocol_combo(GtkCellRendererCombo *combo,
2296  gchar *path_string,
2297  GtkTreeIter *new_iter,
2298  gpointer user_data)
2299 {
2300  SHPDUMPERCONFIG *dumper_table_config;
2301  gchar *geocol_name;
2302  GtkTreeIter iter;
2303  GtkListStore *model;
2304  gpointer gptr;
2305 
2306  /* Get the existing geo column name */
2307  gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path_string);
2308  gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter,
2309  EXPORT_POINTER_COLUMN, &gptr,
2310  EXPORT_GEOMETRY_COLUMN, &geocol_name,
2312  -1);
2313 
2314  /* If the geocol_name is NULL then there was no geo column so exit */
2315  if (!geocol_name)
2316  return;
2317 
2318  /* Otherwise update the geo column name in the config and the model */
2319  gtk_tree_model_get(GTK_TREE_MODEL(model), new_iter, TABLECHOOSER_GEOCOL_COMBO_TEXT, &geocol_name, -1);
2320  dumper_table_config = (SHPDUMPERCONFIG *)gptr;
2321 
2322  if (dumper_table_config->geo_col_name)
2323  {
2324  free(dumper_table_config->geo_col_name);
2325 
2326  dumper_table_config->geo_col_name = strdup(geocol_name);
2327  }
2328 
2329  gtk_list_store_set(export_table_list_store, &iter,
2330  EXPORT_GEOMETRY_COLUMN, geocol_name,
2331  -1);
2332 
2333  return;
2334 }
2335 
2336 static void
2337 pgui_action_handle_dumper_edit(GtkCellRendererText *renderer,
2338  gchar *path,
2339  gchar *new_text,
2340  gpointer column)
2341 {
2342  GtkTreeIter iter;
2343  gpointer gptr;
2344  gint columnindex;
2345  SHPDUMPERCONFIG *dumper_table_config;
2346 
2347  /* Empty doesn't fly */
2348  if (strlen(new_text) == 0)
2349  return;
2350 
2351  /* Update the model with the current edit change */
2352  columnindex = *(gint *)column;
2353  gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path);
2354  gtk_list_store_set(export_table_list_store, &iter, columnindex, new_text, -1);
2355 
2356  /* Grab the SHPDUMPERCONFIG from the EXPORT_POINTER_COLUMN for the list store */
2357  gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
2358  dumper_table_config = (SHPDUMPERCONFIG *)gptr;
2359 
2360  /* Update the configuration from the current UI data */
2361  update_dumper_table_config_from_listview_iter(&iter, dumper_table_config);
2362 
2363  /* Now refresh the listview UI row with the new configuration */
2364  gtk_list_store_set(export_table_list_store, &iter,
2365  EXPORT_SCHEMA_COLUMN, dumper_table_config->schema,
2366  EXPORT_TABLE_COLUMN, dumper_table_config->table,
2367  EXPORT_GEOMETRY_COLUMN, dumper_table_config->geo_col_name,
2368  EXPORT_FILENAME_COLUMN, dumper_table_config->shp_file,
2369  -1);
2370 
2371  return;
2372 }
2373 
2374 /* === Connection Window functions === */
2375 
2376 /* Set the connection details UI from the current configuration */
2377 static void
2379 {
2380  if (conn->username)
2381  gtk_entry_set_text(GTK_ENTRY(entry_pg_user), conn->username);
2382  else
2383  gtk_entry_set_text(GTK_ENTRY(entry_pg_user), "");
2384 
2385  if (conn->password)
2386  gtk_entry_set_text(GTK_ENTRY(entry_pg_pass), conn->password);
2387  else
2388  gtk_entry_set_text(GTK_ENTRY(entry_pg_pass), "");
2389 
2390  if (conn->host)
2391  gtk_entry_set_text(GTK_ENTRY(entry_pg_host), conn->host);
2392  else
2393  gtk_entry_set_text(GTK_ENTRY(entry_pg_host), "");
2394 
2395  if (conn->port)
2396  gtk_entry_set_text(GTK_ENTRY(entry_pg_port), conn->port);
2397  else
2398  gtk_entry_set_text(GTK_ENTRY(entry_pg_port), "");
2399 
2400  if (conn->database)
2401  gtk_entry_set_text(GTK_ENTRY(entry_pg_db), conn->database);
2402  else
2403  gtk_entry_set_text(GTK_ENTRY(entry_pg_db), "");
2404 
2405  return;
2406 }
2407 
2408 /* Set the current connection configuration from the connection details UI */
2409 static void
2411 {
2412  const char *text;
2413 
2414  text = gtk_entry_get_text(GTK_ENTRY(entry_pg_user));
2415  if (conn->username)
2416  free(conn->username);
2417 
2418  if (strlen(text))
2419  conn->username = strdup(text);
2420  else
2421  conn->username = NULL;
2422 
2423  text = gtk_entry_get_text(GTK_ENTRY(entry_pg_pass));
2424  if (conn->password)
2425  free(conn->password);
2426 
2427  if (strlen(text))
2428  conn->password = strdup(text);
2429  else
2430  conn->password = NULL;
2431 
2432  text = gtk_entry_get_text(GTK_ENTRY(entry_pg_host));
2433  if (conn->host)
2434  free(conn->host);
2435 
2436  if (strlen(text))
2437  conn->host = strdup(text);
2438  else
2439  conn->host = NULL;
2440 
2441  text = gtk_entry_get_text(GTK_ENTRY(entry_pg_port));
2442  if (conn->port)
2443  free(conn->port);
2444 
2445  if (strlen(text))
2446  conn->port = strdup(text);
2447  else
2448  conn->port = NULL;
2449 
2450  text = gtk_entry_get_text(GTK_ENTRY(entry_pg_db));
2451  if (conn->database)
2452  free(conn->database);
2453 
2454  if (strlen(text))
2455  conn->database = strdup(text);
2456  else
2457  conn->database = NULL;
2458 
2459  return;
2460 }
2461 
2462 /*
2463  * Open the connection details dialog
2464  */
2465 static void
2466 pgui_action_connection_details(GtkWidget *widget, gpointer data)
2467 {
2468  /* Update the UI with the current options */
2470 
2471  gtk_widget_show_all(GTK_WIDGET(window_conn));
2472  return;
2473 }
2474 
2475 /* Validate the connection, returning true or false */
2476 static int
2478 {
2479  int i;
2480 
2481  if (conn->port && strlen(conn->port))
2482  {
2483  for (i = 0; i < strlen(conn->port); i++)
2484  {
2485  if (!isdigit(conn->port[i]))
2486  {
2487  pgui_seterr(_("The connection port must be numeric!"));
2488  return 0;
2489  }
2490  }
2491  }
2492 
2493  return 1;
2494 }
2495 
2496 static void
2497 pgui_sanitize_connection_string(char *connection_string)
2498 {
2499  char *ptr = strstr(connection_string, "password");
2500  if ( ptr )
2501  {
2502  ptr += 10;
2503  while ( *ptr != '\'' && *ptr != '\0' )
2504  {
2505  /* If we find a \, hide both it and the next character */
2506  if ( *ptr == '\\' )
2507  *ptr++ = '*';
2508 
2509  *ptr++ = '*';
2510  }
2511  }
2512  return;
2513 }
2514 
2515 /*
2516  * We retain the ability to explicitly request a test of the connection
2517  * parameters. This is the button signal handler to do so.
2518  */
2519 static void
2520 pgui_action_connection_okay(GtkWidget *widget, gpointer data)
2521 {
2522  /* Update the configuration structure from the form */
2524 
2525  /* Make sure have a valid connection first */
2526  if (!pgui_validate_connection())
2527  {
2529  return;
2530  }
2531 
2532  if (!connection_test())
2533  {
2534  pgui_logf(_("Connection failed."));
2535 
2536  /* If the connection failed, display a warning before closing */
2537  pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
2539  }
2540  else
2541  {
2542  pgui_logf(_("Connection succeeded."));
2543  }
2544 
2545 
2546  /* Hide the window after the test */
2547  gtk_widget_hide(GTK_WIDGET(window_conn));
2548 }
2549 
2550 
2551 /* === Window creation functions === */
2552 
2553 static void
2555 {
2556  const char *authors[] =
2557  {
2558  "Paul Ramsey <pramsey@cleverelephant.ca>",
2559  "Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>",
2560  "Mark Leslie <mark.s.leslie@gmail.com>",
2561  NULL
2562  };
2563 
2564 
2565 
2566  dialog_about = gtk_about_dialog_new();
2567  gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog_about), _("PostGIS Shapefile Import/Export Manager"));
2568  gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog_about), POSTGIS_LIB_VERSION);
2569  gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog_about), "http://postgis.net/");
2570  gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(dialog_about), authors);
2571 }
2572 
2573 static void
2575 {
2576  GtkFileFilter *file_filter_shape;
2577 
2578  /* Create the dialog */
2579  dialog_filechooser = gtk_file_chooser_dialog_new( _("Select a Shape File"), GTK_WINDOW (window_main),
2580  GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
2581 
2582  /* Filter for .shp files */
2583  file_filter_shape = gtk_file_filter_new();
2584  gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.shp");
2585  gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("Shape Files (*.shp)"));
2586  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_filechooser), file_filter_shape);
2587 
2588  /* Filter for .dbf files */
2589  file_filter_shape = gtk_file_filter_new();
2590  gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.dbf");
2591  gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("DBF Files (*.dbf)"));
2592  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_filechooser), file_filter_shape);
2593 
2594  /* Allow multiple files to be selected */
2595  g_object_set(dialog_filechooser, "select-multiple", TRUE, NULL);
2596 
2597  return;
2598 }
2599 
2600 static void
2602 {
2603  GtkFileFilter *file_filter_shape;
2604 
2605  /* Create the dialog */
2606  dialog_folderchooser = gtk_file_chooser_dialog_new( _("Select an output folder"), GTK_WINDOW (window_main),
2607  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
2608 
2609  /* Filter for .shp files */
2610  file_filter_shape = gtk_file_filter_new();
2611  gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.shp");
2612  gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("Shape Files (*.shp)"));
2613  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_folderchooser), file_filter_shape);
2614 
2615  /* Filter for .dbf files */
2616  file_filter_shape = gtk_file_filter_new();
2617  gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.dbf");
2618  gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("DBF Files (*.dbf)"));
2619  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_folderchooser), file_filter_shape);
2620 
2621  return;
2622 }
2623 
2624 static void
2626 {
2627  GtkWidget *vbox_progress, *table_progress;
2628 
2629  dialog_progress = gtk_dialog_new_with_buttons(_("Working..."), GTK_WINDOW(window_main), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
2630 
2631  gtk_window_set_modal(GTK_WINDOW(dialog_progress), TRUE);
2632  gtk_window_set_keep_above(GTK_WINDOW(dialog_progress), TRUE);
2633  gtk_window_set_default_size(GTK_WINDOW(dialog_progress), 640, -1);
2634 
2635  /* Use a vbox as the base container */
2636  vbox_progress = gtk_dialog_get_content_area(GTK_DIALOG(dialog_progress));
2637  gtk_box_set_spacing(GTK_BOX(vbox_progress), 15);
2638 
2639  /* Create a table within the vbox */
2640  table_progress = gtk_table_new(2, 1, TRUE);
2641  gtk_container_set_border_width (GTK_CONTAINER (table_progress), 12);
2642  gtk_table_set_row_spacings(GTK_TABLE(table_progress), 5);
2643  gtk_table_set_col_spacings(GTK_TABLE(table_progress), 10);
2644 
2645  /* Text for the progress bar */
2646  label_progress = gtk_label_new("");
2647  gtk_table_attach_defaults(GTK_TABLE(table_progress), label_progress, 0, 1, 0, 1);
2648 
2649  /* Progress bar for the import */
2650  progress = gtk_progress_bar_new();
2651  gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(progress), GTK_PROGRESS_LEFT_TO_RIGHT);
2652  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.0);
2653  gtk_table_attach_defaults(GTK_TABLE(table_progress), progress, 0, 1, 1, 2);
2654 
2655  /* Add the table to the vbox */
2656  gtk_box_pack_start(GTK_BOX(vbox_progress), table_progress, FALSE, FALSE, 0);
2657 
2658  /* Add signal for cancel button */
2659  g_signal_connect(dialog_progress, "response", G_CALLBACK(pgui_action_progress_cancel), dialog_progress);
2660 
2661  /* Make sure we catch a delete event too */
2662  gtk_signal_connect(GTK_OBJECT(dialog_progress), "delete_event", GTK_SIGNAL_FUNC(pgui_action_progress_delete), NULL);
2663 
2664  return;
2665 }
2666 
2667 static void
2668 pgui_create_options_dialog_add_label(GtkWidget *table, const char *str, gfloat alignment, int row)
2669 {
2670  GtkWidget *align = gtk_alignment_new(alignment, 0.5, 0.0, 1.0);
2671  GtkWidget *label = gtk_label_new(str);
2672  gtk_table_attach_defaults(GTK_TABLE(table), align, 1, 3, row, row + 1);
2673  gtk_container_add(GTK_CONTAINER (align), label);
2674 }
2675 
2676 static void
2678 {
2679  GtkWidget *table_options;
2680  GtkWidget *align_options_center;
2681  static int text_width = 12;
2682 
2683  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);
2684 
2685  gtk_window_set_modal (GTK_WINDOW(dialog_loader_options), TRUE);
2686  gtk_window_set_keep_above (GTK_WINDOW(dialog_loader_options), TRUE);
2687  gtk_window_set_default_size (GTK_WINDOW(dialog_loader_options), 180, -1);
2688 
2689  table_options = gtk_table_new(7, 3, TRUE);
2690  gtk_container_set_border_width (GTK_CONTAINER (table_options), 12);
2691  gtk_table_set_row_spacings(GTK_TABLE(table_options), 5);
2692  gtk_table_set_col_spacings(GTK_TABLE(table_options), 10);
2693 
2694  pgui_create_options_dialog_add_label(table_options, _("DBF file character encoding"), 0.0, 0);
2695  entry_options_encoding = gtk_entry_new();
2696  gtk_entry_set_width_chars(GTK_ENTRY(entry_options_encoding), text_width);
2697  gtk_table_attach_defaults(GTK_TABLE(table_options), entry_options_encoding, 0, 1, 0, 1 );
2698 
2699  pgui_create_options_dialog_add_label(table_options, _("Preserve case of column names"), 0.0, 1);
2700  checkbutton_loader_options_preservecase = gtk_check_button_new();
2701  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2702  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 1, 2 );
2703  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_preservecase);
2704 
2705  pgui_create_options_dialog_add_label(table_options, _("Do not create 'bigint' columns"), 0.0, 2);
2706  checkbutton_loader_options_forceint = gtk_check_button_new();
2707  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2708  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 2, 3 );
2709  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_forceint);
2710 
2711  pgui_create_options_dialog_add_label(table_options, _("Create spatial index automatically after load"), 0.0, 3);
2712  checkbutton_loader_options_autoindex = gtk_check_button_new();
2713  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2714  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 3, 4 );
2715  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_autoindex);
2716 
2717  pgui_create_options_dialog_add_label(table_options, _("Load only attribute (dbf) data"), 0.0, 4);
2718  checkbutton_loader_options_dbfonly = gtk_check_button_new();
2719  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2720  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 4, 5 );
2721  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_dbfonly);
2722 
2723  pgui_create_options_dialog_add_label(table_options, _("Load data using COPY rather than INSERT"), 0.0, 5);
2724  checkbutton_loader_options_dumpformat = gtk_check_button_new();
2725  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 0.0 );
2726  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 5, 6 );
2727  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_dumpformat);
2728 
2729  pgui_create_options_dialog_add_label(table_options, _("Load into GEOGRAPHY column"), 0.0, 6);
2730  checkbutton_loader_options_geography = gtk_check_button_new();
2731  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2732  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 6, 7 );
2733  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_geography);
2734 
2735  pgui_create_options_dialog_add_label(table_options, _("Generate simple geometries instead of MULTI geometries"), 0.0, 7);
2736  checkbutton_loader_options_simplegeoms = gtk_check_button_new();
2737  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2738  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 7, 8 );
2739  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_simplegeoms);
2740 
2741  /* Catch the response from the dialog */
2742  g_signal_connect(dialog_loader_options, "response", G_CALLBACK(pgui_action_loader_options_close), dialog_loader_options);
2743  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_loader_options)->vbox), table_options, FALSE, FALSE, 0);
2744 
2745  /* Hook the delete event so we don't destroy the dialog (just hide) if cancelled */
2746  gtk_signal_connect(GTK_OBJECT(dialog_loader_options), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
2747 }
2748 
2749 static void
2751 {
2752  GtkWidget *table_options;
2753  GtkWidget *align_options_center;
2754 
2755  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);
2756 
2757  gtk_window_set_modal (GTK_WINDOW(dialog_dumper_options), TRUE);
2758  gtk_window_set_keep_above (GTK_WINDOW(dialog_dumper_options), TRUE);
2759  gtk_window_set_default_size (GTK_WINDOW(dialog_dumper_options), 180, -1);
2760 
2761  table_options = gtk_table_new(3, 3, TRUE);
2762  gtk_container_set_border_width (GTK_CONTAINER (table_options), 12);
2763  gtk_table_set_row_spacings(GTK_TABLE(table_options), 5);
2764  gtk_table_set_col_spacings(GTK_TABLE(table_options), 10);
2765 
2766  pgui_create_options_dialog_add_label(table_options, _("Include gid column in the exported table"), 0.0, 0);
2767  checkbutton_dumper_options_includegid = gtk_check_button_new();
2768  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2769  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 0, 1 );
2770  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_includegid);
2771 
2772  pgui_create_options_dialog_add_label(table_options, _("Preserve case of column names"), 0.0, 1);
2773  checkbutton_dumper_options_keep_fieldname_case = gtk_check_button_new();
2774  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2775  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 1, 2 );
2776  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_keep_fieldname_case);
2777 
2778  pgui_create_options_dialog_add_label(table_options, _("Escape column names"), 0.0, 2);
2779  checkbutton_dumper_options_unescapedattrs = gtk_check_button_new();
2780  align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
2781  gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 2, 3 );
2782  gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_unescapedattrs);
2783 
2784  /* Catch the response from the dialog */
2785  g_signal_connect(dialog_dumper_options, "response", G_CALLBACK(pgui_action_dumper_options_close), dialog_dumper_options);
2786  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_dumper_options)->vbox), table_options, FALSE, FALSE, 0);
2787 
2788  /* Hook the delete event so we don't destroy the dialog (just hide) if cancelled */
2789  gtk_signal_connect(GTK_OBJECT(dialog_dumper_options), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
2790 }
2791 
2792 /*
2793  * This function creates the UI artefacts for the file list table and hooks
2794  * up all the pretty signals.
2795  */
2796 static void
2798 {
2799  GtkWidget *vbox_tree, *table_progress;
2800  GtkWidget *sw, *label;
2801  GtkTreeSelection *chooser_selection;
2802 
2803  /* Create the main top level window with a 10px border */
2804  dialog_tablechooser = gtk_dialog_new_with_buttons(_("Table selection"), GTK_WINDOW(window_main),
2805  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
2806 
2807  gtk_container_set_border_width(GTK_CONTAINER(dialog_tablechooser), 10);
2808  gtk_window_set_position(GTK_WINDOW(dialog_tablechooser), GTK_WIN_POS_CENTER);
2809 
2810  vbox_tree = gtk_dialog_get_content_area(GTK_DIALOG(dialog_tablechooser));
2811 
2812  /* Setup a model */
2813  chooser_table_list_store = gtk_list_store_new(TABLECHOOSER_N_COLUMNS,
2814  G_TYPE_STRING,
2815  G_TYPE_STRING,
2816  GTK_TYPE_TREE_MODEL,
2817  G_TYPE_STRING,
2818  G_TYPE_INT);
2819 
2820  /* Because we want to do selective filtering on the treeview content, we now implement a GtkTreeModel
2821  filter on top of the original tree model */
2822  chooser_filtered_table_list_store = (GtkListStore *)gtk_tree_model_filter_new(GTK_TREE_MODEL(chooser_table_list_store), NULL);
2823  gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(chooser_filtered_table_list_store),
2824  (GtkTreeModelFilterVisibleFunc)table_chooser_visibility_func, NULL, NULL);
2825 
2826  /* Create the view and such */
2827  chooser_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(chooser_filtered_table_list_store));
2828  chooser_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(chooser_tree));
2829  gtk_tree_selection_set_mode(chooser_selection, GTK_SELECTION_MULTIPLE);
2830 
2831  /* Make the tree view in a scrollable window */
2832  sw = gtk_scrolled_window_new(NULL, NULL);
2833  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2834  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
2835  gtk_widget_set_size_request(sw, 320, 240);
2836 
2837  gtk_box_pack_start(GTK_BOX(vbox_tree), sw, FALSE, FALSE, 10);
2838  gtk_container_add(GTK_CONTAINER(sw), chooser_tree);
2839 
2840  /* Schema Field */
2841  chooser_schema_renderer = gtk_cell_renderer_text_new();
2842  g_object_set(chooser_schema_renderer, "editable", TRUE, NULL);
2843  g_signal_connect(G_OBJECT(chooser_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), NULL);
2844  chooser_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
2846  "text",
2848  NULL);
2849  g_object_set(chooser_schema_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
2850  gtk_tree_view_append_column(GTK_TREE_VIEW(chooser_tree), chooser_schema_column);
2851 
2852  /* Table Field */
2853  chooser_table_renderer = gtk_cell_renderer_text_new();
2854  g_object_set(chooser_table_renderer, "editable", FALSE, NULL);
2855  g_signal_connect(G_OBJECT(chooser_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), NULL);
2856  chooser_table_column = gtk_tree_view_column_new_with_attributes(_("Table"),
2858  "text",
2860  NULL);
2861  g_object_set(chooser_table_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
2862  gtk_tree_view_append_column(GTK_TREE_VIEW(chooser_tree), chooser_table_column);
2863 
2864  /* Create table to hold the tick-box and text */
2865  table_progress = gtk_table_new(1, 2, FALSE);
2866  gtk_container_set_border_width (GTK_CONTAINER (table_progress), 0);
2867  gtk_table_set_row_spacings(GTK_TABLE(table_progress), 0);
2868  gtk_table_set_col_spacings(GTK_TABLE(table_progress), 0);
2869 
2870  checkbutton_chooser_geoonly = gtk_check_button_new();
2871  gtk_table_attach(GTK_TABLE(table_progress), checkbutton_chooser_geoonly, 0, 1, 0, 1, GTK_SHRINK, GTK_FILL, 0, 0);
2872  label = gtk_label_new(_("Only show tables with geo columns"));
2873  gtk_table_attach(GTK_TABLE(table_progress), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 5, 0);
2874  g_signal_connect(G_OBJECT(checkbutton_chooser_geoonly), "toggled", G_CALLBACK(pgui_action_chooser_toggle_show_geocolumn), NULL);
2875  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_chooser_geoonly), TRUE);
2876 
2877  /* Attach table to the vbox */
2878  gtk_box_pack_start(GTK_BOX(vbox_tree), table_progress, FALSE, FALSE, 10);
2879 
2880  return;
2881 }
2882 
2883 
2884 /*
2885  * This function creates the UI artefacts for the file list table and hooks
2886  * up all the pretty signals.
2887  */
2888 static void
2889 pgui_create_import_file_table(GtkWidget *import_list_frame)
2890 {
2891  GtkWidget *vbox_tree;
2892  GtkWidget *sw;
2893  GtkTreeIter iter;
2894  gint *column_indexes;
2895 
2896  gtk_container_set_border_width (GTK_CONTAINER (import_list_frame), 0);
2897 
2898  vbox_tree = gtk_vbox_new(FALSE, 15);
2899  gtk_container_set_border_width(GTK_CONTAINER(vbox_tree), 5);
2900  gtk_container_add(GTK_CONTAINER(import_list_frame), vbox_tree);
2901 
2902  /* Setup a model */
2903  import_file_list_store = gtk_list_store_new(IMPORT_N_COLUMNS,
2904  G_TYPE_POINTER,
2905  G_TYPE_STRING,
2906  G_TYPE_STRING,
2907  G_TYPE_STRING,
2908  G_TYPE_STRING,
2909  G_TYPE_STRING,
2910  G_TYPE_STRING,
2911  G_TYPE_BOOLEAN);
2912 
2913  /* Create the view and such */
2914  import_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(import_file_list_store));
2915 
2916  /* GTK has a slightly brain-dead API in that you can't directly find
2917  the column being used by a GtkCellRenderer when using the same
2918  callback to handle multiple fields; hence we manually store this
2919  information here and pass a pointer to the column index into
2920  the signal handler */
2921  column_indexes = g_malloc(sizeof(gint) * IMPORT_N_COLUMNS);
2922 
2923  /* Make the tree view in a scrollable window */
2924  sw = gtk_scrolled_window_new(NULL, NULL);
2925  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2926  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
2927  gtk_widget_set_size_request(sw, -1, 150);
2928 
2929  gtk_box_pack_start(GTK_BOX(vbox_tree), sw, TRUE, TRUE, 0);
2930  gtk_container_add(GTK_CONTAINER (sw), import_tree);
2931 
2932  /* Place the "Add File" button below the list view */
2933  add_file_button = gtk_button_new_with_label(_("Add File"));
2934  gtk_container_add (GTK_CONTAINER (vbox_tree), add_file_button);
2935 
2936  /* Filename Field */
2937  import_filename_renderer = gtk_cell_renderer_text_new();
2938  g_object_set(import_filename_renderer, "editable", FALSE, NULL);
2940  g_signal_connect(G_OBJECT(import_filename_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_FILENAME_COLUMN]);
2941  import_filename_column = gtk_tree_view_column_new_with_attributes(_("Shapefile"),
2943  "text",
2945  NULL);
2946  g_object_set(import_filename_column, "resizable", TRUE, NULL);
2947  gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_filename_column);
2948 
2949  /* Schema Field */
2950  import_schema_renderer = gtk_cell_renderer_text_new();
2951  g_object_set(import_schema_renderer, "editable", TRUE, NULL);
2952  column_indexes[IMPORT_SCHEMA_COLUMN] = IMPORT_SCHEMA_COLUMN;
2953  g_signal_connect(G_OBJECT(import_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_SCHEMA_COLUMN]);
2954  import_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
2956  "text",
2958  NULL);
2959  g_object_set(import_schema_column, "resizable", TRUE, "expand", TRUE, NULL);
2960  gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_schema_column);
2961 
2962  /* Table Field */
2963  import_table_renderer = gtk_cell_renderer_text_new();
2964  g_object_set(import_table_renderer, "editable", TRUE, NULL);
2965  column_indexes[IMPORT_TABLE_COLUMN] = IMPORT_TABLE_COLUMN;
2966  g_signal_connect(G_OBJECT(import_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_TABLE_COLUMN]);
2967  import_table_column = gtk_tree_view_column_new_with_attributes(_("Table"),
2969  "text",
2971  NULL);
2972  g_object_set(import_table_column, "resizable", TRUE, "expand", TRUE, NULL);
2973  gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_table_column);
2974 
2975  /* Geo column field */
2976  import_geom_column_renderer = gtk_cell_renderer_text_new();
2977  g_object_set(import_geom_column_renderer, "editable", TRUE, NULL);
2979  g_signal_connect(G_OBJECT(import_geom_column_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_GEOMETRY_COLUMN]);
2980  import_geom_column = gtk_tree_view_column_new_with_attributes(_("Geo Column"),
2982  "text",
2984  NULL);
2985  g_object_set(import_geom_column, "resizable", TRUE, "expand", TRUE, NULL);
2986  gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_geom_column);
2987 
2988  /* SRID Field */
2989  import_srid_renderer = gtk_cell_renderer_text_new();
2990  g_object_set(import_srid_renderer, "editable", TRUE, NULL);
2991  column_indexes[IMPORT_SRID_COLUMN] = IMPORT_SRID_COLUMN;
2992  g_signal_connect(G_OBJECT(import_srid_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_SRID_COLUMN]);
2993  import_srid_column = gtk_tree_view_column_new_with_attributes("SRID",
2995  "text",
2997  NULL);
2998  g_object_set(import_srid_column, "resizable", TRUE, "expand", TRUE, NULL);
2999  gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_srid_column);
3000 
3001  /* Mode Combo Field */
3003  G_TYPE_STRING,
3004  G_TYPE_CHAR);
3005 
3006  gtk_list_store_insert(loader_mode_combo_list, &iter, CREATE_MODE);
3007  gtk_list_store_set(loader_mode_combo_list, &iter,
3008  LOADER_MODE_COMBO_TEXT, _("Create"),
3010  -1);
3011  gtk_list_store_insert(loader_mode_combo_list, &iter, APPEND_MODE);
3012  gtk_list_store_set(loader_mode_combo_list, &iter,
3013  LOADER_MODE_COMBO_TEXT, _("Append"),
3015  -1);
3016  gtk_list_store_insert(loader_mode_combo_list, &iter, DELETE_MODE);
3017  gtk_list_store_set(loader_mode_combo_list, &iter,
3018  LOADER_MODE_COMBO_TEXT, _("Delete"),
3020  -1);
3021  gtk_list_store_insert(loader_mode_combo_list, &iter, PREPARE_MODE);
3022  gtk_list_store_set(loader_mode_combo_list, &iter,
3023  LOADER_MODE_COMBO_TEXT, _("Prepare"),
3025  -1);
3026  loader_mode_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(loader_mode_combo_list));
3027  import_mode_renderer = gtk_cell_renderer_combo_new();
3028  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(loader_mode_combo),
3030  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(loader_mode_combo),
3031  import_mode_renderer, "text", 0);
3032  g_object_set(import_mode_renderer,
3033  "model", loader_mode_combo_list,
3034  "editable", TRUE,
3035  "has-entry", FALSE,
3036  "text-column", LOADER_MODE_COMBO_TEXT,
3037  NULL);
3038  import_mode_column = gtk_tree_view_column_new_with_attributes(_("Mode"),
3040  "text",
3042  NULL);
3043  gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_mode_column);
3044  gtk_combo_box_set_active(GTK_COMBO_BOX(loader_mode_combo), 1);
3045  g_object_set(import_mode_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3046 
3047  g_signal_connect (G_OBJECT(import_mode_renderer), "changed", G_CALLBACK(pgui_action_handle_tree_combo), NULL);
3048 
3049  /* Remove Field */
3050  import_remove_renderer = gtk_cell_renderer_toggle_new();
3051  g_object_set(import_remove_renderer, "activatable", TRUE, NULL);
3052  g_signal_connect(G_OBJECT(import_remove_renderer), "toggled", G_CALLBACK (pgui_action_handle_file_remove), NULL);
3053  import_remove_column = gtk_tree_view_column_new_with_attributes("Rm",
3054  import_remove_renderer, NULL);
3055  g_object_set(import_remove_column, "resizable", TRUE, "expand", FALSE, "fixed-width", 64, "sizing", GTK_TREE_VIEW_COLUMN_FIXED, NULL);
3056  gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_remove_column);
3057 
3058  g_signal_connect (G_OBJECT (add_file_button), "clicked", G_CALLBACK (pgui_action_open_file_dialog), NULL);
3059 
3060  /* Drag n Drop wiring */
3061  GtkTargetEntry drop_types[] =
3062  {
3063  { "text/uri-list", 0, 0}
3064  };
3065 
3066  gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
3067  gtk_drag_dest_set(GTK_WIDGET(import_tree),
3068  GTK_DEST_DEFAULT_ALL,
3069  drop_types, n_drop_types,
3070  GDK_ACTION_COPY);
3071  g_signal_connect(G_OBJECT(import_tree), "drag_data_received",
3072  G_CALLBACK(pgui_action_handle_file_drop), NULL);
3073 }
3074 
3075 /*
3076  * This function creates the UI artefacts for the file list table and hooks
3077  * up all the pretty signals.
3078  */
3079 static void
3080 pgui_create_export_table_table(GtkWidget *export_list_frame)
3081 {
3082  GtkWidget *vbox_tree;
3083  GtkWidget *sw;
3084  gint *column_indexes;
3085 
3086  gtk_container_set_border_width (GTK_CONTAINER (export_list_frame), 0);
3087 
3088  vbox_tree = gtk_vbox_new(FALSE, 15);
3089  gtk_container_set_border_width(GTK_CONTAINER(vbox_tree), 5);
3090  gtk_container_add(GTK_CONTAINER(export_list_frame), vbox_tree);
3091 
3092  /* Setup a model */
3093  export_table_list_store = gtk_list_store_new(EXPORT_N_COLUMNS,
3094  G_TYPE_POINTER,
3095  G_TYPE_STRING,
3096  G_TYPE_STRING,
3097  G_TYPE_STRING,
3098  GTK_TYPE_TREE_MODEL,
3099  G_TYPE_STRING,
3100  G_TYPE_BOOLEAN);
3101 
3102  /* Create the view and such */
3103  export_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(export_table_list_store));
3104 
3105  /* GTK has a slightly brain-dead API in that you can't directly find
3106  the column being used by a GtkCellRenderer when using the same
3107  callback to handle multiple fields; hence we manually store this
3108  information here and pass a pointer to the column index into
3109  the signal handler */
3110  column_indexes = g_malloc(sizeof(gint) * EXPORT_N_COLUMNS);
3111 
3112  /* Make the tree view in a scrollable window */
3113  sw = gtk_scrolled_window_new(NULL, NULL);
3114  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
3115  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
3116  gtk_widget_set_size_request(sw, -1, 150);
3117 
3118  gtk_box_pack_start(GTK_BOX(vbox_tree), sw, TRUE, TRUE, 0);
3119  gtk_container_add(GTK_CONTAINER (sw), export_tree);
3120 
3121  /* Place the "Add Table" button below the list view */
3122  add_table_button = gtk_button_new_with_label(_("Add Table"));
3123  gtk_container_add (GTK_CONTAINER (vbox_tree), add_table_button);
3124 
3125  /* Schema Field */
3126  export_schema_renderer = gtk_cell_renderer_text_new();
3127  g_object_set(export_schema_renderer, "editable", FALSE, NULL);
3128  column_indexes[EXPORT_SCHEMA_COLUMN] = EXPORT_SCHEMA_COLUMN;
3129  g_signal_connect(G_OBJECT(export_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[EXPORT_SCHEMA_COLUMN]);
3130  export_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
3132  "text",
3134  NULL);
3135  g_object_set(export_schema_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3136  gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_schema_column);
3137 
3138  /* Table Field */
3139  export_table_renderer = gtk_cell_renderer_text_new();
3140  g_object_set(export_table_renderer, "editable", FALSE, NULL);
3141  column_indexes[EXPORT_TABLE_COLUMN] = EXPORT_TABLE_COLUMN;
3142  g_signal_connect(G_OBJECT(export_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[EXPORT_TABLE_COLUMN]);
3143  export_table_column = gtk_tree_view_column_new_with_attributes(_("Table"),
3145  "text",
3147  NULL);
3148  g_object_set(export_table_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3149  gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_table_column);
3150 
3151  /* Geo column field */
3152  export_geom_column_combo = gtk_combo_box_new();
3153  export_geom_column_renderer = gtk_cell_renderer_combo_new();
3154  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(export_geom_column_combo),
3156  g_object_set(export_geom_column_renderer,
3157  "editable", TRUE,
3158  "has-entry", FALSE,
3159  "text-column", TABLECHOOSER_GEOCOL_COMBO_TEXT,
3160  NULL);
3161  export_geom_column = gtk_tree_view_column_new_with_attributes(_("Geo Column"),
3163  "model",
3165  "text",
3167  NULL);
3168  g_object_set(export_geom_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3169  gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_geom_column);
3170  g_signal_connect (G_OBJECT(export_geom_column_renderer), "changed", G_CALLBACK(pgui_action_handle_table_geocol_combo), NULL);
3171 
3172  /* Filename Field */
3173  export_filename_renderer = gtk_cell_renderer_text_new();
3174  g_object_set(export_filename_renderer, "editable", TRUE, NULL);
3176  g_signal_connect(G_OBJECT(export_filename_renderer), "edited", G_CALLBACK(pgui_action_handle_dumper_edit), &column_indexes[EXPORT_FILENAME_COLUMN]);
3177  export_filename_column = gtk_tree_view_column_new_with_attributes(_("Filename"),
3179  "text",
3181  NULL);
3182  g_object_set(export_filename_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
3183  gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_filename_column);
3184 
3185  /* Remove Field */
3186  export_remove_renderer = gtk_cell_renderer_toggle_new();
3187  g_object_set(export_remove_renderer, "activatable", TRUE, NULL);
3188  g_signal_connect(G_OBJECT(export_remove_renderer), "toggled", G_CALLBACK (pgui_action_handle_table_remove), NULL);
3189  export_remove_column = gtk_tree_view_column_new_with_attributes("Rm",
3190  export_remove_renderer, NULL);
3191  g_object_set(export_remove_column, "resizable", TRUE, "expand", FALSE, "fixed-width", 64, "sizing", GTK_TREE_VIEW_COLUMN_FIXED, NULL);
3192  gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_remove_column);
3193 
3194  g_signal_connect (G_OBJECT (add_table_button), "clicked", G_CALLBACK (pgui_action_open_table_dialog), NULL);
3195 }
3196 
3197 static void
3199 {
3200  /* Default text width */
3201  static int text_width = 12;
3202 
3203  /* Vbox container */
3204  GtkWidget *vbox;
3205 
3206  /* Reusable label handle */
3207  GtkWidget *label;
3208 
3209  /* PgSQL section */
3210  GtkWidget *frame_pg, *table_pg;
3211 
3212  /* OK button */
3213  GtkWidget *button_okay;
3214 
3215  /* Create the main top level window with a 10px border */
3216  window_conn = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3217  gtk_container_set_border_width(GTK_CONTAINER(window_conn), 10);
3218  gtk_window_set_title(GTK_WINDOW(window_conn), _("PostGIS connection"));
3219  gtk_window_set_position(GTK_WINDOW(window_conn), GTK_WIN_POS_CENTER);
3220  gtk_window_set_modal(GTK_WINDOW(window_conn), TRUE);
3221 
3222  /* Use a vbox as the base container */
3223  vbox = gtk_vbox_new(FALSE, 15);
3224 
3225  /*
3226  ** PostGIS info in a table
3227  */
3228  frame_pg = gtk_frame_new(_("PostGIS Connection"));
3229  table_pg = gtk_table_new(5, 3, TRUE);
3230  gtk_container_set_border_width (GTK_CONTAINER (table_pg), 8);
3231  gtk_table_set_col_spacings(GTK_TABLE(table_pg), 7);
3232  gtk_table_set_row_spacings(GTK_TABLE(table_pg), 3);
3233 
3234  /* User name row */
3235  label = gtk_label_new(_("Username:"));
3236  entry_pg_user = gtk_entry_new();
3237  gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 0, 1 );
3238  gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_user, 1, 3, 0, 1 );
3239 
3240  /* Password row */
3241  label = gtk_label_new(_("Password:"));
3242  entry_pg_pass = gtk_entry_new();
3243  gtk_entry_set_visibility( GTK_ENTRY(entry_pg_pass), FALSE);
3244  gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 1, 2 );
3245  gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_pass, 1, 3, 1, 2 );
3246 
3247  /* Host and port row */
3248  label = gtk_label_new(_("Server Host:"));
3249  entry_pg_host = gtk_entry_new();
3250  gtk_entry_set_width_chars(GTK_ENTRY(entry_pg_host), text_width);
3251  gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 2, 3 );
3252  gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_host, 1, 2, 2, 3 );
3253 
3254  entry_pg_port = gtk_entry_new();
3255  gtk_entry_set_width_chars(GTK_ENTRY(entry_pg_port), 8);
3256  gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_port, 2, 3, 2, 3 );
3257 
3258  /* Database row */
3259  label = gtk_label_new(_("Database:"));
3260  entry_pg_db = gtk_entry_new();
3261  gtk_table_attach_defaults(GTK_TABLE(table_pg), label, 0, 1, 3, 4 );
3262  gtk_table_attach_defaults(GTK_TABLE(table_pg), entry_pg_db, 1, 3, 3, 4 );
3263 
3264  /* Add table into containing frame */
3265  gtk_container_add(GTK_CONTAINER(frame_pg), table_pg);
3266 
3267  /* Add frame into containing vbox */
3268  gtk_container_add(GTK_CONTAINER(window_conn), vbox);
3269 
3270  /* Add the vbox into the window */
3271  gtk_container_add(GTK_CONTAINER(vbox), frame_pg);
3272 
3273  /* Create a simple "OK" button for the dialog */
3274  button_okay = gtk_button_new_with_label(_("OK"));
3275  gtk_container_add(GTK_CONTAINER(vbox), button_okay);
3276  g_signal_connect(G_OBJECT(button_okay), "clicked", G_CALLBACK(pgui_action_connection_okay), NULL);
3277 
3278  /* Hook the delete event so we don't destroy the dialog (only hide it) if cancelled */
3279  gtk_signal_connect(GTK_OBJECT(window_conn), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
3280 
3281  return;
3282 }
3283 
3284 static void
3286 {
3287  /* Main widgets */
3288  GtkWidget *vbox_main, *vbox_loader, *vbox_dumper;
3289 
3290  /* PgSQL section */
3291  GtkWidget *frame_pg, *import_list_frame, *export_list_frame, *frame_log;
3292  GtkWidget *button_pg_conn;
3293 
3294  /* Notebook */
3295  GtkWidget *notebook;
3296 
3297  /* Button section */
3298  GtkWidget *loader_hbox_buttons, *loader_button_options, *loader_button_import, *loader_button_cancel, *loader_button_about;
3299  GtkWidget *dumper_hbox_buttons, *dumper_button_options, *dumper_button_export, *dumper_button_cancel, *dumper_button_about;
3300 
3301  /* Log section */
3302  GtkWidget *scrolledwindow_log;
3303 
3304  /* Create the main top level window with a 10px border */
3305  window_main = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3306  gtk_container_set_border_width(GTK_CONTAINER(window_main), 10);
3307  gtk_window_set_title(GTK_WINDOW(window_main), _("PostGIS Shapefile Import/Export Manager"));
3308  gtk_window_set_position(GTK_WINDOW(window_main), GTK_WIN_POS_CENTER_ALWAYS);
3309  gtk_window_set_resizable(GTK_WINDOW(window_main), FALSE);
3310 
3311  /* Open it a bit wider so that both the label and title show up */
3312  gtk_window_set_default_size(GTK_WINDOW(window_main), 180, 500);
3313 
3314  /* Connect the destroy event of the window with our pgui_quit function
3315  * When the window is about to be destroyed we get a notification and
3316  * stop the main GTK loop
3317  */
3318  g_signal_connect(G_OBJECT(window_main), "destroy", G_CALLBACK(pgui_quit), NULL);
3319 
3320  /* Connection row */
3321  frame_pg = gtk_frame_new(_("PostGIS Connection"));
3322 
3323  /* Test button row */
3324  button_pg_conn = gtk_button_new_with_label(_("View connection details..."));
3325  g_signal_connect(G_OBJECT(button_pg_conn), "clicked", G_CALLBACK(pgui_action_connection_details), NULL);
3326  gtk_container_set_border_width(GTK_CONTAINER(button_pg_conn), 10);
3327  gtk_container_add(GTK_CONTAINER(frame_pg), button_pg_conn);
3328 
3329  /*
3330  * GTK Notebook for selecting import/export
3331  */
3332  notebook = gtk_notebook_new();
3333 
3334  /*
3335  ** Shape file selector
3336  */
3337  import_list_frame = gtk_frame_new(_("Import List"));
3338  pgui_create_import_file_table(import_list_frame);
3339 
3340  /*
3341  ** Row of action buttons
3342  */
3343  loader_hbox_buttons = gtk_hbox_new(TRUE, 15);
3344  gtk_container_set_border_width (GTK_CONTAINER (loader_hbox_buttons), 0);
3345 
3346  /* Create the buttons themselves */
3347  loader_button_options = gtk_button_new_with_label(_("Options..."));
3348  loader_button_import = gtk_button_new_with_label(_("Import"));
3349  loader_button_cancel = gtk_button_new_with_label(_("Cancel"));
3350  loader_button_about = gtk_button_new_with_label(_("About"));
3351 
3352  /* Add actions to the buttons */
3353  g_signal_connect (G_OBJECT (loader_button_import), "clicked", G_CALLBACK (pgui_action_import), NULL);
3354  g_signal_connect (G_OBJECT (loader_button_options), "clicked", G_CALLBACK (pgui_action_loader_options_open), NULL);
3355  g_signal_connect (G_OBJECT (loader_button_cancel), "clicked", G_CALLBACK (pgui_action_cancel), NULL);
3356  g_signal_connect (G_OBJECT (loader_button_about), "clicked", G_CALLBACK (pgui_action_about_open), NULL);
3357 
3358  /* And insert the buttons into the hbox */
3359  gtk_box_pack_start(GTK_BOX(loader_hbox_buttons), loader_button_options, TRUE, TRUE, 0);
3360  gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_cancel, TRUE, TRUE, 0);
3361  gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_about, TRUE, TRUE, 0);
3362  gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_import, TRUE, TRUE, 0);
3363 
3364  /*
3365  ** Table selector
3366  */
3367  export_list_frame = gtk_frame_new(_("Export List"));
3368  pgui_create_export_table_table(export_list_frame);
3369 
3370  /*
3371  ** Row of action buttons
3372  */
3373  dumper_hbox_buttons = gtk_hbox_new(TRUE, 15);
3374  gtk_container_set_border_width (GTK_CONTAINER (dumper_hbox_buttons), 0);
3375 
3376  /* Create the buttons themselves */
3377  dumper_button_options = gtk_button_new_with_label(_("Options..."));
3378  dumper_button_export = gtk_button_new_with_label(_("Export"));
3379  dumper_button_cancel = gtk_button_new_with_label(_("Cancel"));
3380  dumper_button_about = gtk_button_new_with_label(_("About"));
3381 
3382  /* Add actions to the buttons */
3383  g_signal_connect (G_OBJECT (dumper_button_export), "clicked", G_CALLBACK (pgui_action_export), NULL);
3384  g_signal_connect (G_OBJECT (dumper_button_options), "clicked", G_CALLBACK (pgui_action_dumper_options_open), NULL);
3385  g_signal_connect (G_OBJECT (dumper_button_cancel), "clicked", G_CALLBACK (pgui_action_cancel), NULL);
3386  g_signal_connect (G_OBJECT (dumper_button_about), "clicked", G_CALLBACK (pgui_action_about_open), NULL);
3387 
3388  /* And insert the buttons into the hbox */
3389  gtk_box_pack_start(GTK_BOX(dumper_hbox_buttons), dumper_button_options, TRUE, TRUE, 0);
3390  gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_cancel, TRUE, TRUE, 0);
3391  gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_about, TRUE, TRUE, 0);
3392  gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_export, TRUE, TRUE, 0);
3393 
3394  /*
3395  ** Log window
3396  */
3397  frame_log = gtk_frame_new(_("Log Window"));
3398  gtk_container_set_border_width (GTK_CONTAINER (frame_log), 0);
3399  gtk_widget_set_size_request(frame_log, -1, 200);
3400  textview_log = gtk_text_view_new();
3401  textbuffer_log = gtk_text_buffer_new(NULL);
3402  scrolledwindow_log = gtk_scrolled_window_new(NULL, NULL);
3403  gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolledwindow_log), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3404  gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview_log), textbuffer_log);
3405  gtk_container_set_border_width (GTK_CONTAINER (textview_log), 5);
3406  gtk_text_view_set_editable(GTK_TEXT_VIEW(textview_log), FALSE);
3407  gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview_log), FALSE);
3408  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview_log), GTK_WRAP_WORD);
3409  gtk_container_add (GTK_CONTAINER (scrolledwindow_log), textview_log);
3410  gtk_container_add (GTK_CONTAINER (frame_log), scrolledwindow_log);
3411 
3412  /*
3413  ** Main window
3414  */
3415  vbox_main = gtk_vbox_new(FALSE, 10);
3416  gtk_container_set_border_width (GTK_CONTAINER (vbox_main), 0);
3417 
3418  /* Add the loader frames into the notebook page */
3419  vbox_loader = gtk_vbox_new(FALSE, 10);
3420  gtk_container_set_border_width(GTK_CONTAINER(vbox_loader), 10);
3421 
3422  gtk_box_pack_start(GTK_BOX(vbox_loader), import_list_frame, FALSE, TRUE, 0);
3423  gtk_box_pack_start(GTK_BOX(vbox_loader), loader_hbox_buttons, FALSE, FALSE, 0);
3424  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_loader, gtk_label_new(_("Import")));
3425 
3426  /* Add the dumper frames into the notebook page */
3427  vbox_dumper = gtk_vbox_new(FALSE, 10);
3428  gtk_container_set_border_width(GTK_CONTAINER(vbox_dumper), 10);
3429 
3430  gtk_box_pack_start(GTK_BOX(vbox_dumper), export_list_frame, FALSE, TRUE, 0);
3431  gtk_box_pack_start(GTK_BOX(vbox_dumper), dumper_hbox_buttons, FALSE, FALSE, 0);
3432  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_dumper, gtk_label_new(_("Export")));
3433 
3434  /* Add the frames into the main vbox */
3435  gtk_box_pack_start(GTK_BOX(vbox_main), frame_pg, FALSE, TRUE, 0);
3436  gtk_box_pack_start(GTK_BOX(vbox_main), notebook, FALSE, TRUE, 0);
3437  gtk_box_pack_start(GTK_BOX(vbox_main), frame_log, TRUE, TRUE, 0);
3438 
3439  /* and insert the vbox into the main window */
3440  gtk_container_add(GTK_CONTAINER(window_main), vbox_main);
3441 
3442  /* make sure that everything, window and label, are visible */
3443  gtk_widget_show_all(window_main);
3444 
3445  return;
3446 }
3447 
3448 static void
3450 {
3451  printf("RCSID: %s RELEASE: %s\n", S2P_RCSID, POSTGIS_VERSION);
3452  printf("USAGE: shp2pgsql-gui [options]\n");
3453  printf("OPTIONS:\n");
3454  printf(" -U <username>\n");
3455  printf(" -W <password>\n");
3456  printf(" -h <host>\n");
3457  printf(" -p <port>\n");
3458  printf(" -d <database>\n");
3459  printf(" -? Display this help screen\n");
3460 }
3461 
3462 int
3463 main(int argc, char *argv[])
3464 {
3465  int c;
3466 
3467 #ifdef ENABLE_NLS
3468  setlocale (LC_ALL, "");
3469  bindtextdomain (PACKAGE, PGSQL_LOCALEDIR);
3470  textdomain (PACKAGE);
3471 #endif
3472 
3473  /* Parse command line options and set configuration */
3478 
3479  /* Here we override any defaults for the GUI */
3483 
3484  conn = malloc(sizeof(SHPCONNECTIONCONFIG));
3485  memset(conn, 0, sizeof(SHPCONNECTIONCONFIG));
3486 
3487  /* Here we override any defaults for the connection */
3488  conn->host = strdup("localhost");
3489  conn->port = strdup("5432");
3490 
3491  while ((c = pgis_getopt(argc, argv, "U:p:W:d:h:")) != -1)
3492  {
3493  switch (c)
3494  {
3495  case 'U':
3496  conn->username = strdup(pgis_optarg);
3497  break;
3498  case 'p':
3499  conn->port = strdup(pgis_optarg);
3500  break;
3501  case 'W':
3502  conn->password = strdup(pgis_optarg);
3503  break;
3504  case 'd':
3505  conn->database = strdup(pgis_optarg);
3506  break;
3507  case 'h':
3508  conn->host = strdup(pgis_optarg);
3509  break;
3510  default:
3511  usage();
3512  free(conn);
3514  exit(0);
3515  }
3516  }
3517 
3518  /* initialize the GTK stack */
3519  gtk_init(&argc, &argv);
3520 
3521  /* set up the user interface */
3531 
3532  /* start the main loop */
3533  gtk_main();
3534 
3535  /* Free the configuration */
3536  free(conn);
3538 
3539  return 0;
3540 }
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:2551
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 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)
static void pgui_create_progress_dialog()
#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