]> granicus.if.org Git - postgis/commitdiff
Add shapefile dumper (table export) to the shp2pgsql GUI. This closes ticket #1480.
authorMark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
Sat, 4 Feb 2012 00:39:24 +0000 (00:39 +0000)
committerMark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
Sat, 4 Feb 2012 00:39:24 +0000 (00:39 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@9025 b70326c6-7e19-0410-871a-916f4a2858ee

loader/shp2pgsql-gui.c

index 07e94a1eba910729c4ec1fed386f14adb46093f9..426e73d12a58172dac6086d5e72f393362edf5d4 100644 (file)
@@ -45,30 +45,49 @@ static void update_conn_ui_from_conn_config(void);
 static GtkWidget *window_main = NULL;
 
 static GtkWidget *textview_log = NULL;
-static GtkWidget *add_file_button = NULL;
 static GtkTextBuffer *textbuffer_log = NULL;
 
-/* Main window (listview) */
-GtkListStore *list_store;
-GtkWidget *tree;
-GtkCellRenderer *filename_renderer;
-GtkCellRenderer *schema_renderer;
-GtkCellRenderer *table_renderer;
-GtkCellRenderer *geom_column_renderer;
-GtkCellRenderer *srid_renderer;
-GtkCellRenderer *mode_renderer;
-GtkCellRenderer *remove_renderer;
-
-GtkTreeViewColumn *filename_column;
-GtkTreeViewColumn *schema_column;
-GtkTreeViewColumn *table_column;
-GtkTreeViewColumn *geom_column;
-GtkTreeViewColumn *srid_column;
-GtkTreeViewColumn *mode_column;
-GtkTreeViewColumn *remove_column;
-
-GtkWidget *mode_combo = NULL;
-GtkListStore *combo_list;
+/* Main import window (listview) */
+GtkListStore *import_file_list_store;
+GtkWidget *import_tree;
+GtkCellRenderer *import_filename_renderer;
+GtkCellRenderer *import_schema_renderer;
+GtkCellRenderer *import_table_renderer;
+GtkCellRenderer *import_geom_column_renderer;
+GtkCellRenderer *import_srid_renderer;
+GtkCellRenderer *import_mode_renderer;
+GtkCellRenderer *import_remove_renderer;
+
+GtkTreeViewColumn *import_filename_column;
+GtkTreeViewColumn *import_schema_column;
+GtkTreeViewColumn *import_table_column;
+GtkTreeViewColumn *import_geom_column;
+GtkTreeViewColumn *import_srid_column;
+GtkTreeViewColumn *import_mode_column;
+GtkTreeViewColumn *import_remove_column;
+
+static GtkWidget *add_file_button = NULL;
+
+GtkWidget *loader_mode_combo = NULL;
+GtkListStore *loader_mode_combo_list;
+
+/* Main export window (listview) */
+GtkListStore *export_table_list_store;
+GtkWidget *export_tree;
+GtkWidget *export_geom_column_combo;
+GtkCellRenderer *export_schema_renderer;
+GtkCellRenderer *export_table_renderer;
+GtkCellRenderer *export_geom_column_renderer;
+GtkCellRenderer *export_filename_renderer;
+GtkCellRenderer *export_remove_renderer;
+
+GtkTreeViewColumn *export_schema_column;
+GtkTreeViewColumn *export_table_column;
+GtkTreeViewColumn *export_geom_column;
+GtkTreeViewColumn *export_filename_column;
+GtkTreeViewColumn *export_remove_column;
+
+static GtkWidget *add_table_button = NULL;
 
 /* PostgreSQL database connection window */
 static GtkWidget *window_conn = NULL;
@@ -79,49 +98,67 @@ static GtkWidget *entry_pg_host = NULL;
 static GtkWidget *entry_pg_port = NULL;
 static GtkWidget *entry_pg_db = NULL;
 
-/* Options window */
-static GtkWidget *dialog_options = NULL;
+/* Loader options window */
+static GtkWidget *dialog_loader_options = NULL;
 static GtkWidget *entry_options_encoding = NULL;
-static GtkWidget *checkbutton_options_preservecase = NULL;
-static GtkWidget *checkbutton_options_forceint = NULL;
-static GtkWidget *checkbutton_options_autoindex = NULL;
-static GtkWidget *checkbutton_options_dbfonly = NULL;
-static GtkWidget *checkbutton_options_dumpformat = NULL;
-static GtkWidget *checkbutton_options_geography = NULL;
+static GtkWidget *checkbutton_loader_options_preservecase = NULL;
+static GtkWidget *checkbutton_loader_options_forceint = NULL;
+static GtkWidget *checkbutton_loader_options_autoindex = NULL;
+static GtkWidget *checkbutton_loader_options_dbfonly = NULL;
+static GtkWidget *checkbutton_loader_options_dumpformat = NULL;
+static GtkWidget *checkbutton_loader_options_geography = NULL;
+
+/* Dumper options window */
+static GtkWidget *dialog_dumper_options = NULL;
+static GtkWidget *checkbutton_dumper_options_includegid = NULL;
+static GtkWidget *checkbutton_dumper_options_keep_fieldname_case = NULL;
+static GtkWidget *checkbutton_dumper_options_unescapedattrs = NULL;
 
 /* About dialog */
 static GtkWidget *dialog_about = NULL;
 
 /* File chooser */
 static GtkWidget *dialog_filechooser = NULL;
+static GtkWidget *dialog_folderchooser = NULL;
 
 /* Progress dialog */
 static GtkWidget *dialog_progress = NULL;
 static GtkWidget *progress = NULL;
 static GtkWidget *label_progress = NULL;
 
+/* Table chooser dialog */
+static GtkWidget *dialog_tablechooser = NULL;
+GtkListStore *chooser_filtered_table_list_store;
+GtkListStore *chooser_table_list_store;
+GtkWidget *chooser_tree;
+GtkCellRenderer *chooser_schema_renderer;
+GtkCellRenderer *chooser_table_renderer;
+GtkTreeViewColumn *chooser_schema_column;
+GtkTreeViewColumn *chooser_table_column;
+static GtkWidget *checkbutton_chooser_geoonly = NULL;
+
 /* Other items */
 static int valid_connection = 0;
 
 /* Constants for the list view etc. */
 enum
 {
-       POINTER_COLUMN,
-       FILENAME_COLUMN,
-       SCHEMA_COLUMN,
-       TABLE_COLUMN,
-       GEOMETRY_COLUMN,
-       SRID_COLUMN,
-       MODE_COLUMN,
-       REMOVE_COLUMN,
-       N_COLUMNS
+       IMPORT_POINTER_COLUMN,
+       IMPORT_FILENAME_COLUMN,
+       IMPORT_SCHEMA_COLUMN,
+       IMPORT_TABLE_COLUMN,
+       IMPORT_GEOMETRY_COLUMN,
+       IMPORT_SRID_COLUMN,
+       IMPORT_MODE_COLUMN,
+       IMPORT_REMOVE_COLUMN,
+       IMPORT_N_COLUMNS
 };
 
 enum
 {
-       COMBO_TEXT,
-       COMBO_OPTION_CHAR,
-       COMBO_COLUMNS
+       LOADER_MODE_COMBO_TEXT,
+       LOADER_MODE_COMBO_OPTION_CHAR,
+       LOADER_MODE_COMBO_COLUMNS
 };
 
 enum
@@ -132,14 +169,42 @@ enum
        PREPARE_MODE
 };
 
+enum
+{
+       EXPORT_POINTER_COLUMN,
+       EXPORT_SCHEMA_COLUMN,
+       EXPORT_TABLE_COLUMN,
+       EXPORT_GEOMETRY_COLUMN,
+       EXPORT_GEOMETRY_LISTSTORE_COLUMN,
+       EXPORT_FILENAME_COLUMN,
+       EXPORT_REMOVE_COLUMN,
+       EXPORT_N_COLUMNS
+};
+
+enum
+{
+       TABLECHOOSER_SCHEMA_COLUMN,
+       TABLECHOOSER_TABLE_COLUMN,
+       TABLECHOOSER_GEO_LISTSTORE_COLUMN,
+       TABLECHOOSER_GEO_COLUMN,
+       TABLECHOOSER_HASGEO_COLUMN,
+       TABLECHOOSER_N_COLUMNS
+};
+
+enum
+{
+       TABLECHOOSER_GEOCOL_COMBO_TEXT,
+       TABLECHOOSER_GEOCOL_COMBO_COLUMNS
+};
+
 /* Other */
 static char *pgui_errmsg = NULL;
 static PGconn *pg_connection = NULL;
-static SHPLOADERSTATE *state = NULL;
 static SHPCONNECTIONCONFIG *conn = NULL;
 static SHPLOADERCONFIG *global_loader_config = NULL;
+static SHPDUMPERCONFIG *global_dumper_config = NULL;
 
-static volatile int import_running = FALSE;
+static volatile int is_running = FALSE;
 
 /* Local prototypes */
 static void pgui_sanitize_connection_string(char *connection_string);
@@ -364,24 +429,24 @@ update_filename_field_width(void)
        
        /* Loop through the list store to find the maximum length of an entry */
        max_width = 0;
-       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter);
+       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
        while (is_valid)
        {
                /* Grab the length of the filename entry in characters */
-               gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, FILENAME_COLUMN, &filename, -1);
+               gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_FILENAME_COLUMN, &filename, -1);
                if (strlen(filename) > max_width)
                        max_width = strlen(filename);
                
                /* Get next entry */
-               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter);
+               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
        }
        
        /* Note the layout manager will handle the minimum size for us; we just need to be concerned with
           making sure we don't exceed a maximum limit */
        if (max_width > SHAPEFIELDMAXWIDTH)
-               g_object_set(filename_renderer, "width-chars", SHAPEFIELDMAXWIDTH, NULL);
+               g_object_set(import_filename_renderer, "width-chars", SHAPEFIELDMAXWIDTH, NULL);
        else
-               g_object_set(filename_renderer, "width-chars", -1, NULL);
+               g_object_set(import_filename_renderer, "width-chars", -1, NULL);
        
        return;
 }
@@ -445,7 +510,7 @@ static void
 pgui_action_progress_cancel(GtkDialog *dialog, gint response_id, gpointer user_data) 
 {
        /* Stop the current import */
-       import_running = FALSE;
+       is_running = FALSE;
 
        return;
 }
@@ -454,25 +519,25 @@ static gint
 pgui_action_progress_delete(GtkWidget *widget, GdkEvent *event, gpointer data)
 {
        /* Stop the current import */
-       import_running = FALSE;
+       is_running = FALSE;
 
        return TRUE;
 }
 
 
-/* === Option Window functions === */
+/* === Loader option Window functions === */
 
 /* Update the specified SHPLOADERCONFIG with the global settings from the Options dialog */
 static void
 update_loader_config_globals_from_options_ui(SHPLOADERCONFIG *config)
 {
        const char *entry_encoding = gtk_entry_get_text(GTK_ENTRY(entry_options_encoding));
-       gboolean preservecase = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_preservecase));
-       gboolean forceint = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_forceint));
-       gboolean createindex = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_autoindex));
-       gboolean dbfonly = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_dbfonly));
-       gboolean dumpformat = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_dumpformat));
-       gboolean geography = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_options_geography));
+       gboolean preservecase = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_preservecase));
+       gboolean forceint = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_forceint));
+       gboolean createindex = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_autoindex));
+       gboolean dbfonly = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_dbfonly));
+       gboolean dumpformat = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_dumpformat));
+       gboolean geography = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_geography));
 
        if (geography)
        {
@@ -540,17 +605,17 @@ update_loader_config_globals_from_options_ui(SHPLOADERCONFIG *config)
        return;
 }
 
-/* Update the options dialog with the current values from the global config */
+/* Update the loader options dialog with the current values from the global config */
 static void
 update_options_ui_from_loader_config_globals(void)
 {
        gtk_entry_set_text(GTK_ENTRY(entry_options_encoding), global_loader_config->encoding);
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_preservecase), global_loader_config->quoteidentifiers ? TRUE : FALSE);
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_forceint), global_loader_config->forceint4 ? TRUE : FALSE);
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_autoindex), global_loader_config->createindex ? TRUE : FALSE);
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_dbfonly), global_loader_config->readshape ? FALSE : TRUE);
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_dumpformat), global_loader_config->dump_format ? TRUE : FALSE);
-       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_options_geography), global_loader_config->geography ? TRUE : FALSE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_preservecase), global_loader_config->quoteidentifiers ? TRUE : FALSE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_forceint), global_loader_config->forceint4 ? TRUE : FALSE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_autoindex), global_loader_config->createindex ? TRUE : FALSE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_dbfonly), global_loader_config->readshape ? FALSE : TRUE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_dumpformat), global_loader_config->dump_format ? TRUE : FALSE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_loader_options_geography), global_loader_config->geography ? TRUE : FALSE);
        
        return;
 }
@@ -569,27 +634,256 @@ pgui_set_loader_configs_from_options_ui()
 
        /* Now also update the same settings for any existing files already added. We
           do this by looping through all entries and updating their config too. */
-       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter);
+       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
        while (is_valid)
        {
                /* Get the SHPLOADERCONFIG for this file entry */
-               gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, POINTER_COLUMN, &gptr, -1);
+               gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
                loader_file_config = (SHPLOADERCONFIG *)gptr;
                
                /* Update it */
                update_loader_config_globals_from_options_ui(loader_file_config);
                
                /* Get next entry */
-               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter);
+               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
+       }
+       
+       return;
+}
+
+
+/* === Table selection dialog functions === */
+
+/* Load the model with information from the database */
+static void
+update_table_chooser_from_database()
+{
+       PGresult *result, *geocol_result;
+       GtkTreeIter iter, geocol_iter;
+       GtkListStore *dumper_geocol_combo_list;
+       char *connection_string, *sql_form, *query, *schema, *table, *geocol_query, *geocol_name;
+       int hasgeo, i, j;
+       
+       /* Open a connection to the database */
+       connection_string = ShpDumperGetConnectionStringFromConn(conn);
+       pg_connection = PQconnectdb(connection_string);
+       
+       /* Here we find a list of all tables/views that not in a pg_* schema (or information_schema) and
+          we return the schema name, table name and whether or not the table/view contains any geo
+          columns */
+       query = "SELECT tableoids.oid, n.nspname, tableoids.relname, COALESCE((SELECT 1 from pg_attribute WHERE attrelid = tableoids.oid AND atttypid IN (SELECT oid FROM pg_type WHERE typname in ('geometry', 'geography')) LIMIT 1), 0) hasgeo FROM (SELECT c.oid, c.relname, c.relnamespace FROM pg_class c WHERE c.relkind IN ('r', 'v') AND c.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname NOT ILIKE 'pg_%' AND nspname <> 'information_schema')) tableoids, pg_namespace n WHERE tableoids.relnamespace = n.oid ORDER BY n.nspname, tableoids.relname";
+
+       result = PQexec(pg_connection, query);
+
+       /* Free any existing entries in the model */
+       gtk_list_store_clear(chooser_table_list_store);
+       
+       /* Now insert one row for each query result */
+       for (i = 0; i < PQntuples(result); i++)
+       {
+               gtk_list_store_insert_before(chooser_table_list_store, &iter, NULL);
+
+               /* Look up the geo columns; if there are none then we set the field to (None). If we have just one
+                  column then we set the column name directly. If we have more than one then we create a combo
+                  dropdown containing the column names */
+               schema = PQgetvalue(result, i, PQfnumber(result, "nspname"));
+               table = PQgetvalue(result, i, PQfnumber(result, "relname"));
+               
+               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'))";
+               
+               geocol_query = malloc(strlen(sql_form) + strlen(schema) + strlen(table) + 1);
+               sprintf(geocol_query, sql_form, schema, table);
+               
+               geocol_result = PQexec(pg_connection, geocol_query);
+
+               /* Create a combo list loaded with the column names. Note that while we create the model and load
+                  the data here, we don't actually display the geo column in this dialog. Instead we build the
+                  list here so that we can pass to the export table list store when creating a new entry. This
+                  is to ensure that the export table list model can directly represent a SHPDUMPERCONFIG. */
+               dumper_geocol_combo_list = gtk_list_store_new(TABLECHOOSER_GEOCOL_COMBO_COLUMNS, G_TYPE_STRING);
+               
+               if (PQntuples(geocol_result) > 0)
+               {
+                       /* Load the columns into the list store */
+                       for (j = 0; j < PQntuples(geocol_result); j++)
+                       {
+                               geocol_name = PQgetvalue(geocol_result, j, PQfnumber(geocol_result, "attname"));
+                       
+                               gtk_list_store_insert_before(dumper_geocol_combo_list, &geocol_iter, TABLECHOOSER_GEOCOL_COMBO_TEXT);
+                               gtk_list_store_set(dumper_geocol_combo_list, &geocol_iter, 
+                                               TABLECHOOSER_GEOCOL_COMBO_TEXT, geocol_name,
+                                               -1);
+                       }
+               }
+               else
+               {
+                       /* Add a "default" entry */
+                       geocol_name = NULL;
+                       
+                       gtk_list_store_insert_before(dumper_geocol_combo_list, &geocol_iter, TABLECHOOSER_GEOCOL_COMBO_TEXT);
+                       gtk_list_store_set(dumper_geocol_combo_list, &geocol_iter, 
+                                               TABLECHOOSER_GEOCOL_COMBO_TEXT, _("(None)"),
+                                               -1);
+               }       
+
+               /* Free the query result */
+               PQclear(geocol_result);
+
+               /* Free the query string */
+               free(geocol_query);
+               
+               /* Set the list store data */
+               hasgeo = atoi(PQgetvalue(result, i, PQfnumber(result, "hasgeo")));
+               gtk_list_store_set(chooser_table_list_store, &iter,
+                          TABLECHOOSER_SCHEMA_COLUMN, schema,
+                          TABLECHOOSER_TABLE_COLUMN, table,
+                          TABLECHOOSER_GEO_LISTSTORE_COLUMN, dumper_geocol_combo_list,
+                          TABLECHOOSER_GEO_COLUMN, geocol_name,
+                          TABLECHOOSER_HASGEO_COLUMN, hasgeo,
+                          -1);
+       }
+       
+       /* Clear up the result set */
+       PQclear(result);
+       
+       /* Close the existing connection */
+       PQfinish(pg_connection);
+       pg_connection = NULL;
+       
+       return;
+}
+
+/* GtkTreeModelFilter visibility function */
+static gboolean
+table_chooser_visibility_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+       /* First determine whether the hasgeo tickbox is selected or not */
+       gboolean geoonly = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_chooser_geoonly));
+       int hasgeo;
+       
+       /* If unticked then we show all tables */
+       if (!geoonly)
+               return TRUE;
+       else
+       {
+               /* Otherwise we only show the tables with geo columns */
+               gtk_tree_model_get(GTK_TREE_MODEL(model), iter, TABLECHOOSER_HASGEO_COLUMN, &hasgeo, -1);
+               if (hasgeo)
+                       return TRUE;
+               else
+                       return FALSE;
+       }
+       
+       return FALSE;
+}
+
+/* === Dumper option Window functions === */
+
+/* Update the specified SHPDUMPERCONFIG with the global settings from the Options dialog */
+static void
+update_dumper_config_globals_from_options_ui(SHPDUMPERCONFIG *config)
+{
+       gboolean includegid = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_includegid));
+       gboolean keep_fieldname_case = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_keep_fieldname_case));
+       gboolean unescapedattrs = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_unescapedattrs));
+
+       /* Include gid or not */
+       if (includegid)
+               config->includegid = 1;
+       else
+               config->includegid = 0;
+
+       /* Keep fieldname case */
+       if (keep_fieldname_case)
+               config->keep_fieldname_case = 1;
+       else
+               config->keep_fieldname_case = 0;
+
+       /* Escape column names or not */
+       if (unescapedattrs)
+               config->unescapedattrs = 1;
+       else
+               config->unescapedattrs = 0;
+
+       return;
+}
+
+/* Update the options dialog with the current values from the global config */
+static void
+update_options_ui_from_dumper_config_globals(void)
+{
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_includegid), global_dumper_config->includegid ? TRUE : FALSE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_keep_fieldname_case), global_dumper_config->keep_fieldname_case ? TRUE : FALSE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_dumper_options_unescapedattrs), global_dumper_config->unescapedattrs ? TRUE : FALSE);
+       
+       return;
+}
+
+/* Set the global config variables controlled by the options dialogue */
+static void
+pgui_set_dumper_configs_from_options_ui()
+{
+       GtkTreeIter iter;
+       gboolean is_valid;
+       gpointer gptr;
+       SHPDUMPERCONFIG *dumper_table_config;
+       
+       /* First update the global (template) configuration */
+       update_dumper_config_globals_from_options_ui(global_dumper_config);
+
+       /* Now also update the same settings for any existing tables already added. We
+          do this by looping through all entries and updating their config too. */
+       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(export_table_list_store), &iter);
+       while (is_valid)
+       {
+               /* Get the SHPDUMPERCONFIG for this file entry */
+               gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
+               dumper_table_config = (SHPDUMPERCONFIG *)gptr;
+               
+               /* Update it */
+               update_dumper_config_globals_from_options_ui(dumper_table_config);
+               
+               /* Get next entry */
+               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(export_table_list_store), &iter);
        }
+
+       return;
+}
+
+/* Signal handler for ticking/unticking the "only show geo columns" box */
+static void
+pgui_action_chooser_toggle_show_geocolumn(GtkToggleButton *togglebutton, gpointer user_data)
+{
+       /* Simply update the listview filter */
+       gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(chooser_filtered_table_list_store));
        
        return;
 }
 
+static void
+pgui_action_dumper_options_open(GtkWidget *widget, gpointer data)
+{
+       update_options_ui_from_dumper_config_globals();
+       gtk_widget_show_all(dialog_dumper_options);
+       return;
+}
+
+static void
+pgui_action_dumper_options_close(GtkWidget *widget, gint response, gpointer data)
+{
+       /* Only update the configuration if the user hit OK */
+       if (response == GTK_RESPONSE_OK)
+               pgui_set_dumper_configs_from_options_ui();
+       
+       /* Hide the dialog */
+       gtk_widget_hide(dialog_dumper_options);
+       
+       return;
+}
 
 /* === Main window functions === */
 
-/* Given a filename, generate a new configuration and add it to the listview */
+/* Given a filename, generate a new loader configuration */
 static SHPLOADERCONFIG *
 create_new_file_config(const char *filename)
 {
@@ -660,15 +954,15 @@ add_loader_file_config_to_list(SHPLOADERCONFIG *loader_file_config)
        /* Convert SRID into string */
        lw_asprintf(&srid, "%d", loader_file_config->sr_id);
        
-       gtk_list_store_insert_before(list_store, &iter, NULL);
-       gtk_list_store_set(list_store, &iter,
-                          POINTER_COLUMN, loader_file_config,
-                          FILENAME_COLUMN, loader_file_config->shp_file,
-                          SCHEMA_COLUMN, loader_file_config->schema,
-                          TABLE_COLUMN, loader_file_config->table,
-                          GEOMETRY_COLUMN, loader_file_config->geo_col,
-                          SRID_COLUMN, srid,
-                          MODE_COLUMN, _("Create"),
+       gtk_list_store_insert_before(import_file_list_store, &iter, NULL);
+       gtk_list_store_set(import_file_list_store, &iter,
+                          IMPORT_POINTER_COLUMN, loader_file_config,
+                          IMPORT_FILENAME_COLUMN, loader_file_config->shp_file,
+                          IMPORT_SCHEMA_COLUMN, loader_file_config->schema,
+                          IMPORT_TABLE_COLUMN, loader_file_config->table,
+                          IMPORT_GEOMETRY_COLUMN, loader_file_config->geo_col,
+                          IMPORT_SRID_COLUMN, srid,
+                          IMPORT_MODE_COLUMN, _("Create"),
                           -1); 
                   
        /* Update the filename field width */
@@ -707,6 +1001,96 @@ free_loader_config(SHPLOADERCONFIG *config)
        free(config);
 }
 
+/* Given a table selection, generate a new configuration */
+static SHPDUMPERCONFIG *
+create_new_table_config(GtkTreeIter *iter)
+{
+       SHPDUMPERCONFIG *dumper_table_config;
+       gchar *schema, *table, *geocol;
+       gint hasgeo;
+       
+       /* Generate a new configuration by copying the global options first and then
+          adding in the specific values for this table */
+       dumper_table_config = malloc(sizeof(SHPDUMPERCONFIG));
+       memcpy(dumper_table_config, global_dumper_config, sizeof(SHPDUMPERCONFIG));
+       
+       /* Grab the values from the current iter */
+       gtk_tree_model_get(GTK_TREE_MODEL(chooser_filtered_table_list_store), iter, 
+                          TABLECHOOSER_SCHEMA_COLUMN, &schema,
+                          TABLECHOOSER_TABLE_COLUMN, &table,
+                          TABLECHOOSER_GEO_COLUMN, &geocol, 
+                          TABLECHOOSER_HASGEO_COLUMN, &hasgeo,
+                          -1); 
+       
+       /* Set up the values in the SHPDUMPERCONFIG */
+       dumper_table_config->schema = strdup(schema);
+       dumper_table_config->table = strdup(table);
+       
+       /* We also set the filename the same as the table name */
+       dumper_table_config->shp_file = strdup(table);
+               
+       if (hasgeo && geocol)
+               dumper_table_config->geo_col_name = strdup(geocol);
+       else
+               dumper_table_config->geo_col_name = NULL;
+       
+       return dumper_table_config;
+}
+
+/* Given the dumper configuration, add a new row representing this file to the listview. The liststore and iter arguments
+are optional, and enable the user to specify additional information to the view, e.g. geo column multi-choice. */
+static void
+add_dumper_table_config_to_list(SHPDUMPERCONFIG *dumper_table_config, GtkListStore *chooser_liststore, GtkTreeIter *chooser_iter)
+{
+       GtkTreeIter iter;
+       GtkListStore *geocol_liststore;
+       
+       gtk_list_store_insert_before(export_table_list_store, &iter, NULL);
+       gtk_list_store_set(export_table_list_store, &iter,
+                          EXPORT_POINTER_COLUMN, dumper_table_config,
+                          EXPORT_SCHEMA_COLUMN, dumper_table_config->schema,
+                          EXPORT_TABLE_COLUMN, dumper_table_config->table,
+                          EXPORT_GEOMETRY_COLUMN, dumper_table_config->geo_col_name,
+                          EXPORT_FILENAME_COLUMN, dumper_table_config->shp_file,
+                          -1);
+                         
+       /* If we have supplied the table_chooser store for additional information, use it */
+       if (chooser_liststore)
+       {
+               /* Let's add a multi-choice geometry column to the table */
+               gtk_tree_model_get(GTK_TREE_MODEL(chooser_liststore), chooser_iter,
+                                  TABLECHOOSER_GEO_LISTSTORE_COLUMN, &geocol_liststore,
+                                  -1);
+                                  
+               gtk_list_store_set(export_table_list_store, &iter,
+                                  EXPORT_GEOMETRY_LISTSTORE_COLUMN, geocol_liststore,
+                                  -1);
+       }
+       
+       return;
+}
+
+/* Free up the specified SHPDUMPERCONFIG */
+static void
+free_dumper_config(SHPDUMPERCONFIG *config)
+{
+
+       if (config->table)
+               free(config->table);
+
+       if (config->schema)
+               free(config->schema);
+       
+       if (config->geo_col_name)
+               free(config->geo_col_name);
+       
+       if (config->shp_file)
+               free(config->shp_file);
+       
+       /* Free the config itself */
+       free(config);
+}
+
 /* Validate a single DBF column type against a PostgreSQL type: return either TRUE or FALSE depending
    upon whether or not the type is (broadly) compatible */
 static int
@@ -755,6 +1139,7 @@ static int
 validate_remote_loader_columns(SHPLOADERCONFIG *config, PGresult *result)
 {
        ExecStatusType status;
+       SHPLOADERSTATE *state;
        int ntuples;
        char *pg_fieldname, *pg_fieldtype;
        int ret, i, j, found, response = SHPLOADEROK;
@@ -868,29 +1253,29 @@ pgui_action_about_open()
 static void
 pgui_action_cancel(GtkWidget *widget, gpointer data)
 {
-       if (!import_running)
+       if (!is_running)
                pgui_quit(widget, data); /* quit if we're not running */
        else
-               import_running = FALSE;
+               is_running = FALSE;
 }
 
 static void
-pgui_action_options_open(GtkWidget *widget, gpointer data)
+pgui_action_loader_options_open(GtkWidget *widget, gpointer data)
 {
        update_options_ui_from_loader_config_globals();
-       gtk_widget_show_all(dialog_options);
+       gtk_widget_show_all(dialog_loader_options);
        return;
 }
 
 static void
-pgui_action_options_close(GtkWidget *widget, gint response, gpointer data)
+pgui_action_loader_options_close(GtkWidget *widget, gint response, gpointer data)
 {
        /* Only update the configuration if the user hit OK */
        if (response == GTK_RESPONSE_OK)
                pgui_set_loader_configs_from_options_ui();
        
        /* Hide the dialog */
-       gtk_widget_hide(dialog_options);
+       gtk_widget_hide(dialog_loader_options);
        
        return;
 }
@@ -911,21 +1296,105 @@ pgui_action_open_file_dialog(GtkWidget *widget, gpointer data)
        gtk_widget_hide(dialog_filechooser);
 }
 
+static void
+pgui_action_open_table_dialog(GtkWidget *widget, gpointer data)
+{
+       SHPDUMPERCONFIG *dumper_table_config;
+       GtkTreeSelection *chooser_selection;
+       GtkTreeModel *model;
+       GList *selected_rows_list, *selected_row;
+       GtkTreeIter iter;
+       GtkTreePath *tree_path;
+       
+       /* Make sure we can connect to the database first */    
+       if (!connection_test())
+       {
+               pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
+               pgui_raise_error_dialogue();
+
+               /* Open the connections UI for the user */
+               update_conn_ui_from_conn_config();
+
+               gtk_widget_show_all(GTK_WIDGET(window_conn));
+               return;
+       }
+       
+       /* Setup the form */
+       update_table_chooser_from_database();
+       gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(chooser_filtered_table_list_store));
+
+       /* Run the dialog */
+       gtk_widget_show_all(dialog_tablechooser);
+       if (gtk_dialog_run(GTK_DIALOG(dialog_tablechooser)) == GTK_RESPONSE_OK)
+       {
+               /* Create the new dumper configuration based upon the selected iters and add them to the listview */
+               chooser_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(chooser_tree));
+
+               selected_rows_list = gtk_tree_selection_get_selected_rows(chooser_selection, &model);
+               selected_row = g_list_first(selected_rows_list);
+               while (selected_row)
+               {
+                       /* Get the tree iter */
+                       tree_path = (GtkTreePath *)g_list_nth_data(selected_row, 0);
+                       gtk_tree_model_get_iter(model, &iter, tree_path);
+                       
+                       /* Get the config and add it to the list */
+                       dumper_table_config = create_new_table_config(&iter);   
+                       add_dumper_table_config_to_list(dumper_table_config, chooser_filtered_table_list_store, &iter);                 
+                       
+                       /* Get the next row */
+                       selected_row = g_list_next(selected_row);
+               }
+               
+               /* Free the GList */
+               g_list_foreach(selected_row, (GFunc)gtk_tree_path_free, NULL);
+               g_list_free(selected_row);
+       }
+       
+       gtk_widget_hide(dialog_tablechooser);
+}
+
+/*
+ * Signal handler for the remove box.  Performs no user interaction, simply
+ * removes the row from the table.
+ */
+static void
+pgui_action_handle_table_remove(GtkCellRendererToggle *renderer,
+                               gchar *path,
+                               gpointer user_data)
+{
+       GtkTreeIter iter;
+       SHPDUMPERCONFIG *dumper_table_config;
+       gpointer gptr;
+       
+       /* Grab the SHPDUMPERCONFIG from the EXPORT_POINTER_COLUMN for the list store */
+       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path);
+       gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
+       dumper_table_config = (SHPDUMPERCONFIG *)gptr;
+       
+       /* Free the configuration from memory */
+       free_dumper_config(dumper_table_config);
+       
+       /* Remove the row from the list */
+       gtk_list_store_remove(export_table_list_store, &iter);
+}
+
 static void
 pgui_action_import(GtkWidget *widget, gpointer data)
 {      
        SHPLOADERCONFIG *loader_file_config;
+       SHPLOADERSTATE *state;
        gint is_valid;
        gpointer gptr;
        GtkTreeIter iter;
-       char *sql_form, *query, *progress_text = NULL, *progress_shapefile = NULL;
+       char *sql_form, *query, *connection_string, *progress_text = NULL, *progress_shapefile = NULL;
        PGresult *result;
-       char *connection_string = NULL;
+       
        int ret, success, i = 0;
        char *header, *footer, *record;
        
        /* Get the first row of the import list */
-       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter);
+       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
        if (!is_valid)
        {
                pgui_seterr(_("ERROR: You haven't specified any files to import"));
@@ -961,7 +1430,7 @@ pgui_action_import(GtkWidget *widget, gpointer data)
        while (is_valid)
        {
                /* Grab the SHPLOADERCONFIG for this row */
-               gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, POINTER_COLUMN, &gptr, -1);
+               gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
                loader_file_config = (SHPLOADERCONFIG *)gptr;
                
                /* For each entry, we execute a remote query in order to determine the column names
@@ -987,7 +1456,7 @@ pgui_action_import(GtkWidget *widget, gpointer data)
                free(query);
 
                /* Get next entry */
-               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter);
+               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
        }
        
        /* Close our database connection */
@@ -995,11 +1464,11 @@ pgui_action_import(GtkWidget *widget, gpointer data)
 
        
        /* Once we've done the validation pass, now let's load the shapefile */
-       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter);
+       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(import_file_list_store), &iter);
        while (is_valid)
        {
                /* Grab the SHPLOADERCONFIG for this row */
-               gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, POINTER_COLUMN, &gptr, -1);
+               gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
                loader_file_config = (SHPLOADERCONFIG *)gptr;
        
                pgui_logf("\n==============================");
@@ -1008,7 +1477,7 @@ pgui_action_import(GtkWidget *widget, gpointer data)
                /*
                 * Loop through the items in the shapefile
                 */
-               import_running = TRUE;
+               is_running = TRUE;
                success = FALSE;
                
                /* One connection per file, otherwise error handling becomes tricky... */
@@ -1097,7 +1566,7 @@ pgui_action_import(GtkWidget *widget, gpointer data)
                        }
 
                        /* Main loop: iterate through all of the records and send them to stdout */
-                       for (i = 0; i < ShpLoaderGetRecordCount(state) && import_running; i++)
+                       for (i = 0; i < ShpLoaderGetRecordCount(state) && is_running; i++)
                        {
                                ret = ShpLoaderGenerateSQLRowStatement(state, i, &record);
 
@@ -1173,7 +1642,7 @@ pgui_action_import(GtkWidget *widget, gpointer data)
                } /* if (state->config->opt != 'p') */
 
                /* Only continue if we didn't abort part way through */
-               if (import_running)
+               if (is_running)
                {
                        /* Get the footer */
                        ret = ShpLoaderGetSQLFooter(state, &footer);
@@ -1208,7 +1677,7 @@ pgui_action_import(GtkWidget *widget, gpointer data)
 
 import_cleanup:
                /* Import has definitely stopped running */
-               import_running = FALSE;
+               is_running = FALSE;
 
                /* Close the existing connection */
                PQfinish(pg_connection);
@@ -1231,11 +1700,11 @@ import_cleanup:
                        free(progress_shapefile);
                
                /* Get next entry */
-               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter);
+               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(import_file_list_store), &iter);
        }
        
        /* Import has definitely finished */
-       import_running = FALSE;
+       is_running = FALSE;
 
        /* Enable the button once again */
        gtk_widget_set_sensitive(widget, TRUE);
@@ -1257,37 +1726,254 @@ import_cleanup:
        return;
 }
 
-
-/* === ListView functions and signal handlers === */
-
-/* Creates a single file row in the list table given the URI of a file */
 static void
-process_single_uri(char *uri)
-{
-       SHPLOADERCONFIG *loader_file_config;
-       char *filename = NULL;
-       char *hostname;
-       GError *error = NULL;
-
-       if (uri == NULL)
+pgui_action_export(GtkWidget *widget, gpointer data)
+{      
+       SHPDUMPERCONFIG *dumper_table_config;
+       SHPDUMPERSTATE *state;
+       gint is_valid;
+       gpointer gptr;
+       GtkTreeIter iter;
+       char *output_shapefile, *orig_shapefile, *progress_text = NULL;
+       gchar *folder_path;
+       
+       int ret, success, i = 0;
+       
+       /* Get the first row of the import list */
+       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(export_table_list_store), &iter);
+       if (!is_valid)
        {
-               pgui_logf(_("Unable to process drag URI."));
+               pgui_seterr(_("ERROR: You haven't specified any tables to export"));
+               pgui_raise_error_dialogue();
+
                return;
        }
 
-       filename = g_filename_from_uri(uri, &hostname, &error);
-       g_free(uri);
-
-       if (filename == NULL)
+       /* Firstly make sure that we can connect to the database - if we can't then there isn't much
+          point doing anything else... */
+       if (!connection_test())
        {
-               pgui_logf(_("Unable to process filename: %s\n"), error->message);
-               g_error_free(error);
+               pgui_seterr(_("Unable to connect to the database - please check your connection settings"));
+               pgui_raise_error_dialogue();
+
+               /* Open the connections UI for the user */
+               update_conn_ui_from_conn_config();
+
+               gtk_widget_show_all(GTK_WIDGET(window_conn));
                return;
        }
 
-       /* Create a new row in the listview */
-       loader_file_config = create_new_file_config(filename);
-       add_loader_file_config_to_list(loader_file_config);     
+       /* Now open the file selector dialog so the user can specify where they would like the output
+          files to reside */
+       if (gtk_dialog_run(GTK_DIALOG(dialog_folderchooser)) != GTK_RESPONSE_ACCEPT)
+       {
+               gtk_widget_hide(dialog_folderchooser);
+               
+               return;
+       }
+       
+       gtk_widget_hide(dialog_folderchooser);
+       folder_path = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog_folderchooser));
+               
+       /* Loop through each of the files and set the output filename to the path with the table name
+          appended. We do this as a separate pass here in case we wish to do any validation in future. */
+       while (is_valid)
+       {
+               /* Grab the SHPDUMPERCONFIG for this row */
+               gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
+               dumper_table_config = (SHPDUMPERCONFIG *)gptr;
+
+               orig_shapefile = dumper_table_config->shp_file;
+               output_shapefile = malloc(strlen(folder_path) + strlen(dumper_table_config->shp_file) + 2);
+               strcpy(output_shapefile, folder_path);
+               strcat(output_shapefile, G_DIR_SEPARATOR_S);
+               strcat(output_shapefile, dumper_table_config->shp_file);
+
+               dumper_table_config->shp_file = output_shapefile;
+               
+               /* Get next entry */
+               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(export_table_list_store), &iter);
+       }
+               
+       /* Now everything is set up, let's extract the tables */
+       is_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(export_table_list_store), &iter);
+       while (is_valid)
+       {
+               /* Grab the SHPDUMPERCONFIG for this row */
+               gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
+               dumper_table_config = (SHPDUMPERCONFIG *)gptr;
+       
+               pgui_logf("\n==============================");
+               pgui_logf("Exporting with configuration: %s, %s, %s", dumper_table_config->table, dumper_table_config->schema, dumper_table_config->shp_file);
+               
+               /* Export is running */
+               is_running = TRUE;
+               success = FALSE;
+               
+               /* Disable the button to prevent multiple imports running at the same time */
+               gtk_widget_set_sensitive(widget, FALSE);
+
+               /* Allow GTK events to get a look in */
+               while (gtk_events_pending())
+                       gtk_main_iteration();
+       
+               /* Create the state for each configuration */
+               state = ShpDumperCreate(dumper_table_config);
+               state->config->conn = conn;
+               
+               ret = ShpDumperConnectDatabase(state);
+               if (ret != SHPDUMPEROK)
+               {
+                       pgui_seterr("%s", state->message);
+                       pgui_raise_error_dialogue();
+                       
+                       goto export_cleanup;
+               }
+
+               /* Display the progress dialog */
+               gtk_label_set_text(GTK_LABEL(label_progress), _("Initialising..."));
+               gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), 0.0);
+               gtk_widget_show_all(dialog_progress);
+               
+               ret = ShpDumperOpenTable(state);
+               if (ret != SHPDUMPEROK)
+               {
+                       pgui_logf("%s", state->message);
+
+                       if (ret == SHPDUMPERERR)
+                       {
+                               gtk_widget_hide(dialog_progress);
+                               
+                               pgui_seterr("%s", state->message);
+                               pgui_raise_error_dialogue();
+                               
+                               goto export_cleanup;
+                       }
+               }
+
+               /* Update the text */
+               lw_asprintf(&progress_text, _("Exporting table %s (%d records)..."), dumper_table_config->table, ShpDumperGetRecordCount(state));
+               gtk_label_set_text(GTK_LABEL(label_progress), progress_text);
+
+               /* Allow GTK events to get a look in */
+               while (gtk_events_pending())
+                       gtk_main_iteration();
+
+               pgui_logf(_("Done (postgis major version: %d)"), state->pgis_major_version);
+               pgui_logf(_("Output shape: %s"), shapetypename(state->outshptype));
+               
+               for (i = 0; i < ShpDumperGetRecordCount(state) && is_running == TRUE; i++)
+               {
+                       ret = ShpLoaderGenerateShapeRow(state);
+                       if (ret != SHPDUMPEROK)
+                       {
+                               pgui_logf("%s", state->message);
+       
+                               if (ret == SHPDUMPERERR)
+                               {
+                                       gtk_widget_hide(dialog_progress);
+                                       
+                                       pgui_seterr("%s", state->message);
+                                       pgui_raise_error_dialogue();
+                                       
+                                       goto export_cleanup;
+                               }
+                       }
+                       
+                       /* Update the progress bar */
+                       gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), (float)i / ShpDumperGetRecordCount(state));
+
+                       /* Allow GTK events to get a look in */
+                       while (gtk_events_pending())
+                               gtk_main_iteration();   
+               }
+
+               /* Finish the dump */
+               ret = ShpDumperCloseTable(state);
+               if (ret != SHPDUMPEROK)
+               {
+                       pgui_logf("%s", state->message);
+       
+                       if (ret == SHPDUMPERERR)
+                       {
+                               gtk_widget_hide(dialog_progress);
+                               
+                               pgui_seterr("%s", state->message);
+                               pgui_raise_error_dialogue();
+                       }
+               }
+
+               /* Indicate success */
+               if (is_running)
+                       success = TRUE;
+               
+export_cleanup:
+
+               /* Tidy up everything */
+               ShpDumperDestroy(state);
+
+               /* Reset shapefile back to original form (without full path) */
+               dumper_table_config->shp_file = orig_shapefile;
+               
+               /* Get next entry */
+               is_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(export_table_list_store), &iter);
+       }
+       
+       /* Export has definitely finished */
+       is_running = FALSE;
+       if (!success)
+               pgui_logf(_("Table export failed."));
+       else
+               pgui_logf(_("Table export completed."));
+       
+       /* Enable the button once again */
+       gtk_widget_set_sensitive(widget, TRUE);
+
+       /* Silly GTK bug means we have to hide and show the button for it to work again! */
+       gtk_widget_hide(widget);
+       gtk_widget_show(widget);
+
+       /* Hide the progress dialog */
+       gtk_widget_hide(dialog_progress);
+               
+       /* Allow GTK events to get a look in */
+       while (gtk_events_pending())
+               gtk_main_iteration();
+       
+       return;
+}
+
+
+/* === Import ListView functions and signal handlers === */
+
+/* Creates a single file row in the list table given the URI of a file */
+static void
+process_single_uri(char *uri)
+{
+       SHPLOADERCONFIG *loader_file_config;
+       char *filename = NULL;
+       char *hostname;
+       GError *error = NULL;
+
+       if (uri == NULL)
+       {
+               pgui_logf(_("Unable to process drag URI."));
+               return;
+       }
+
+       filename = g_filename_from_uri(uri, &hostname, &error);
+       g_free(uri);
+
+       if (filename == NULL)
+       {
+               pgui_logf(_("Unable to process filename: %s\n"), error->message);
+               g_error_free(error);
+               return;
+       }
+
+       /* Create a new row in the listview */
+       loader_file_config = create_new_file_config(filename);
+       add_loader_file_config_to_list(loader_file_config);     
 
        g_free(filename);
        g_free(hostname);
@@ -1301,11 +1987,11 @@ update_loader_file_config_from_listview_iter(GtkTreeIter *iter, SHPLOADERCONFIG
        gchar *schema, *table, *geo_col, *srid;
        
        /* Grab the main values for this file */
-       gtk_tree_model_get(GTK_TREE_MODEL(list_store), iter,
-               SCHEMA_COLUMN, &schema,
-               TABLE_COLUMN, &table,
-               GEOMETRY_COLUMN, &geo_col,
-               SRID_COLUMN, &srid,
+       gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), iter,
+               IMPORT_SCHEMA_COLUMN, &schema,
+               IMPORT_TABLE_COLUMN, &table,
+               IMPORT_GEOMETRY_COLUMN, &geo_col,
+               IMPORT_SRID_COLUMN, &srid,
                -1);
        
        /* Update the schema */
@@ -1403,13 +2089,13 @@ pgui_action_handle_tree_combo(GtkCellRendererCombo *combo,
        gchar *combo_text;
        gpointer gptr;
        
-       /* Grab the SHPLOADERCONFIG from the POINTER_COLUMN for the list store */
-       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(list_store), &iter, path_string);
-       gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, POINTER_COLUMN, &gptr, -1);
+       /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
+       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path_string);
+       gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
        loader_file_config = (SHPLOADERCONFIG *)gptr;
 
        /* Now grab the row selected within the combo box */
-       gtk_tree_model_get(GTK_TREE_MODEL(combo_list), new_iter, COMBO_OPTION_CHAR, &opt, -1);
+       gtk_tree_model_get(GTK_TREE_MODEL(loader_mode_combo_list), new_iter, LOADER_MODE_COMBO_OPTION_CHAR, &opt, -1);
        
        /* Update the configuration */
        
@@ -1441,8 +2127,8 @@ pgui_action_handle_tree_combo(GtkCellRendererCombo *combo,
        }
        
        /* Update the selection in the listview with the text from the combo */
-       gtk_tree_model_get(GTK_TREE_MODEL(combo_list), new_iter, COMBO_TEXT, &combo_text, -1); 
-       gtk_list_store_set(list_store, &iter, MODE_COLUMN, combo_text, -1);
+       gtk_tree_model_get(GTK_TREE_MODEL(loader_mode_combo_list), new_iter, LOADER_MODE_COMBO_TEXT, &combo_text, -1); 
+       gtk_list_store_set(import_file_list_store, &iter, IMPORT_MODE_COLUMN, combo_text, -1);
        
        return; 
 }
@@ -1455,7 +2141,7 @@ pgui_action_handle_tree_combo(GtkCellRendererCombo *combo,
  * are applied and the various validations called.
  */
 static void
-pgui_action_handle_tree_edit(GtkCellRendererText *renderer,
+pgui_action_handle_loader_edit(GtkCellRendererText *renderer,
                              gchar *path,
                              gchar *new_text,
                              gpointer column)
@@ -1472,11 +2158,11 @@ pgui_action_handle_tree_edit(GtkCellRendererText *renderer,
        
        /* Update the model with the current edit change */
        columnindex = *(gint *)column;
-       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(list_store), &iter, path);
-       gtk_list_store_set(list_store, &iter, columnindex, new_text, -1);
+       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path);
+       gtk_list_store_set(import_file_list_store, &iter, columnindex, new_text, -1);
        
-       /* Grab the SHPLOADERCONFIG from the POINTER_COLUMN for the list store */
-       gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, POINTER_COLUMN, &gptr, -1);
+       /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
+       gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
        loader_file_config = (SHPLOADERCONFIG *)gptr;
        
        /* Update the configuration from the current UI data */
@@ -1485,11 +2171,11 @@ pgui_action_handle_tree_edit(GtkCellRendererText *renderer,
        /* Now refresh the listview UI row with the new configuration */
        lw_asprintf(&srid, "%d", loader_file_config->sr_id);
        
-       gtk_list_store_set(list_store, &iter,
-                          SCHEMA_COLUMN, loader_file_config->schema,
-                          TABLE_COLUMN, loader_file_config->table,
-                          GEOMETRY_COLUMN, loader_file_config->geo_col,
-                          SRID_COLUMN, srid,
+       gtk_list_store_set(import_file_list_store, &iter,
+                          IMPORT_SCHEMA_COLUMN, loader_file_config->schema,
+                          IMPORT_TABLE_COLUMN, loader_file_config->table,
+                          IMPORT_GEOMETRY_COLUMN, loader_file_config->geo_col,
+                          IMPORT_SRID_COLUMN, srid,
                           -1);
 
        return;
@@ -1500,7 +2186,7 @@ pgui_action_handle_tree_edit(GtkCellRendererText *renderer,
  * removes the row from the table.
  */
 static void
-pgui_action_handle_tree_remove(GtkCellRendererToggle *renderer,
+pgui_action_handle_file_remove(GtkCellRendererToggle *renderer,
                                gchar *path,
                                gpointer user_data)
 {
@@ -1508,22 +2194,144 @@ pgui_action_handle_tree_remove(GtkCellRendererToggle *renderer,
        SHPLOADERCONFIG *loader_file_config;
        gpointer gptr;
        
-       /* Grab the SHPLOADERCONFIG from the POINTER_COLUMN for the list store */
-       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(list_store), &iter, path);
-       gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, POINTER_COLUMN, &gptr, -1);
+       /* Grab the SHPLOADERCONFIG from the IMPORT_POINTER_COLUMN for the list store */
+       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(import_file_list_store), &iter, path);
+       gtk_tree_model_get(GTK_TREE_MODEL(import_file_list_store), &iter, IMPORT_POINTER_COLUMN, &gptr, -1);
        loader_file_config = (SHPLOADERCONFIG *)gptr;
        
        /* Free the configuration from memory */
        free_loader_config(loader_file_config);
        
        /* Remove the row from the list */
-       gtk_list_store_remove(list_store, &iter);
+       gtk_list_store_remove(import_file_list_store, &iter);
 
        /* Update the filename field width */
        update_filename_field_width();
 }
 
 
+/* === Export ListView functions and signal handlers === */
+
+/* Update the SHPDUMPERCONFIG to the values currently contained within the iter  */
+static void
+update_dumper_table_config_from_listview_iter(GtkTreeIter *iter, SHPDUMPERCONFIG *dumper_table_config)
+{
+       gchar *schema, *table, *geo_col, *filename;
+       
+       /* Grab the main values for this file */
+       gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), iter,
+               EXPORT_SCHEMA_COLUMN, &schema,
+               EXPORT_TABLE_COLUMN, &table,
+               EXPORT_GEOMETRY_COLUMN, &geo_col,
+               EXPORT_FILENAME_COLUMN, &filename,
+               -1);
+       
+       /* Update the schema */
+       if (dumper_table_config->schema)
+               free(dumper_table_config->schema);
+               
+       dumper_table_config->schema = strdup(schema);
+       
+       /* Update the table */
+       if (dumper_table_config->table)
+               free(dumper_table_config->table);
+               
+       dumper_table_config->table = strdup(table);
+       
+       /* Update the geometry column */
+       if (dumper_table_config->geo_col_name)
+               free(dumper_table_config->geo_col_name);
+               
+       dumper_table_config->geo_col_name = strdup(geo_col);
+
+       /* Update the filename column (default to table name) */
+       if (dumper_table_config->shp_file)
+               free(dumper_table_config->shp_file);
+               
+       dumper_table_config->shp_file = strdup(filename);
+
+       return;
+}
+
+static void
+pgui_action_handle_table_geocol_combo(GtkCellRendererCombo *combo,
+                              gchar *path_string,
+                              GtkTreeIter *new_iter,
+                              gpointer user_data)
+{
+       SHPDUMPERCONFIG *dumper_table_config;
+       gchar *geocol_name;
+       GtkTreeIter iter;
+       GtkListStore *model;
+       gpointer gptr;
+       
+       /* Get the existing geo column name */  
+       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path_string);
+       gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter,
+                          EXPORT_POINTER_COLUMN, &gptr,
+                          EXPORT_GEOMETRY_COLUMN, &geocol_name,
+                          EXPORT_GEOMETRY_LISTSTORE_COLUMN, &model,
+                          -1);
+       
+       /* If the geocol_name is NULL then there was no geo column so exit */
+       if (!geocol_name)
+               return;
+       
+       /* Otherwise update the geo column name in the config and the model */
+       gtk_tree_model_get(GTK_TREE_MODEL(model), new_iter, TABLECHOOSER_GEOCOL_COMBO_TEXT, &geocol_name, -1);
+       dumper_table_config = (SHPDUMPERCONFIG *)gptr;
+       
+       if (dumper_table_config->geo_col_name)
+       {
+               free(dumper_table_config->geo_col_name);
+               
+               dumper_table_config->geo_col_name = strdup(geocol_name);
+       }
+       
+       gtk_list_store_set(export_table_list_store, &iter, 
+                          EXPORT_GEOMETRY_COLUMN, geocol_name,
+                          -1);
+       
+       return;
+}
+
+static void
+pgui_action_handle_dumper_edit(GtkCellRendererText *renderer,
+                             gchar *path,
+                             gchar *new_text,
+                             gpointer column)
+{
+       GtkTreeIter iter;
+       gpointer gptr;
+       gint columnindex;
+       SHPDUMPERCONFIG *dumper_table_config;
+       
+       /* Empty doesn't fly */
+       if (strlen(new_text) == 0)
+               return;
+       
+       /* Update the model with the current edit change */
+       columnindex = *(gint *)column;
+       gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(export_table_list_store), &iter, path);
+       gtk_list_store_set(export_table_list_store, &iter, columnindex, new_text, -1);
+       
+       /* Grab the SHPDUMPERCONFIG from the EXPORT_POINTER_COLUMN for the list store */
+       gtk_tree_model_get(GTK_TREE_MODEL(export_table_list_store), &iter, EXPORT_POINTER_COLUMN, &gptr, -1);
+       dumper_table_config = (SHPDUMPERCONFIG *)gptr;
+       
+       /* Update the configuration from the current UI data */
+       update_dumper_table_config_from_listview_iter(&iter, dumper_table_config);
+       
+       /* Now refresh the listview UI row with the new configuration */
+       gtk_list_store_set(export_table_list_store, &iter,
+                          EXPORT_SCHEMA_COLUMN, dumper_table_config->schema,
+                          EXPORT_TABLE_COLUMN, dumper_table_config->table,
+                          EXPORT_GEOMETRY_COLUMN, dumper_table_config->geo_col_name,
+                          EXPORT_FILENAME_COLUMN, dumper_table_config->shp_file,
+                          -1);
+
+       return;
+}
 
 /* === Connection Window functions === */
 
@@ -1728,7 +2536,7 @@ pgui_create_filechooser_dialog(void)
        GtkFileFilter *file_filter_shape;
        
        /* Create the dialog */
-       dialog_filechooser= gtk_file_chooser_dialog_new( _("Select a Shape File"), GTK_WINDOW (window_main), 
+       dialog_filechooser = gtk_file_chooser_dialog_new( _("Select a Shape File"), GTK_WINDOW (window_main), 
                GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
 
        /* Filter for .shp files */
@@ -1746,6 +2554,30 @@ pgui_create_filechooser_dialog(void)
        return;
 }
 
+static void
+pgui_create_folderchooser_dialog(void)
+{
+       GtkFileFilter *file_filter_shape;
+       
+       /* Create the dialog */
+       dialog_folderchooser = gtk_file_chooser_dialog_new( _("Select an output folder"), GTK_WINDOW (window_main), 
+               GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+
+       /* Filter for .shp files */
+       file_filter_shape = gtk_file_filter_new();
+       gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.shp");
+       gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("Shape Files (*.shp)"));
+       gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_folderchooser), file_filter_shape);
+
+       /* Filter for .dbf files */
+       file_filter_shape = gtk_file_filter_new();
+       gtk_file_filter_add_pattern(GTK_FILE_FILTER(file_filter_shape), "*.dbf");
+       gtk_file_filter_set_name(GTK_FILE_FILTER(file_filter_shape), _("DBF Files (*.dbf)"));
+       gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_folderchooser), file_filter_shape);
+
+       return;
+}
+
 static void
 pgui_create_progress_dialog()
 {
@@ -1799,17 +2631,17 @@ pgui_create_options_dialog_add_label(GtkWidget *table, const char *str, gfloat a
 }
 
 static void
-pgui_create_options_dialog()
+pgui_create_loader_options_dialog()
 {
        GtkWidget *table_options;
        GtkWidget *align_options_center;
        static int text_width = 12;
 
-       dialog_options = gtk_dialog_new_with_buttons(_("Import Options"), GTK_WINDOW(window_main), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+       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);
 
-       gtk_window_set_modal (GTK_WINDOW(dialog_options), TRUE);
-       gtk_window_set_keep_above (GTK_WINDOW(dialog_options), TRUE);
-       gtk_window_set_default_size (GTK_WINDOW(dialog_options), 180, 200);
+       gtk_window_set_modal (GTK_WINDOW(dialog_loader_options), TRUE);
+       gtk_window_set_keep_above (GTK_WINDOW(dialog_loader_options), TRUE);
+       gtk_window_set_default_size (GTK_WINDOW(dialog_loader_options), 180, -1);
 
        table_options = gtk_table_new(7, 3, TRUE);
        gtk_container_set_border_width (GTK_CONTAINER (table_options), 12);
@@ -1822,47 +2654,90 @@ pgui_create_options_dialog()
        gtk_table_attach_defaults(GTK_TABLE(table_options), entry_options_encoding, 0, 1, 0, 1 );
 
        pgui_create_options_dialog_add_label(table_options, _("Preserve case of column names"), 0.0, 1);
-       checkbutton_options_preservecase = gtk_check_button_new();
+       checkbutton_loader_options_preservecase = gtk_check_button_new();
        align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
        gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 1, 2 );
-       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_preservecase);
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_preservecase);
 
        pgui_create_options_dialog_add_label(table_options, _("Do not create 'bigint' columns"), 0.0, 2);
-       checkbutton_options_forceint = gtk_check_button_new();
+       checkbutton_loader_options_forceint = gtk_check_button_new();
        align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
        gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 2, 3 );
-       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_forceint);
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_forceint);
 
        pgui_create_options_dialog_add_label(table_options, _("Create spatial index automatically after load"), 0.0, 3);
-       checkbutton_options_autoindex = gtk_check_button_new();
+       checkbutton_loader_options_autoindex = gtk_check_button_new();
        align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
        gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 3, 4 );
-       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_autoindex);
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_autoindex);
 
        pgui_create_options_dialog_add_label(table_options, _("Load only attribute (dbf) data"), 0.0, 4);
-       checkbutton_options_dbfonly = gtk_check_button_new();
+       checkbutton_loader_options_dbfonly = gtk_check_button_new();
        align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
        gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 4, 5 );
-       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_dbfonly);
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_dbfonly);
 
        pgui_create_options_dialog_add_label(table_options, _("Load data using COPY rather than INSERT"), 0.0, 5);
-       checkbutton_options_dumpformat = gtk_check_button_new();
+       checkbutton_loader_options_dumpformat = gtk_check_button_new();
        align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 0.0 );
        gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 5, 6 );
-       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_dumpformat);
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_dumpformat);
 
        pgui_create_options_dialog_add_label(table_options, _("Load into GEOGRAPHY column"), 0.0, 6);
-       checkbutton_options_geography = gtk_check_button_new();
+       checkbutton_loader_options_geography = gtk_check_button_new();
        align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
        gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 6, 7 );
-       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_options_geography);
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_loader_options_geography);
 
        /* Catch the response from the dialog */
-       g_signal_connect(dialog_options, "response", G_CALLBACK(pgui_action_options_close), dialog_options);
-       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_options)->vbox), table_options, FALSE, FALSE, 0);
+       g_signal_connect(dialog_loader_options, "response", G_CALLBACK(pgui_action_loader_options_close), dialog_loader_options);
+       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_loader_options)->vbox), table_options, FALSE, FALSE, 0);
        
        /* Hook the delete event so we don't destroy the dialog (just hide) if cancelled */
-       gtk_signal_connect(GTK_OBJECT(dialog_options), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
+       gtk_signal_connect(GTK_OBJECT(dialog_loader_options), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
+}
+
+static void
+pgui_create_dumper_options_dialog()
+{
+       GtkWidget *table_options;
+       GtkWidget *align_options_center;
+
+       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);
+
+       gtk_window_set_modal (GTK_WINDOW(dialog_dumper_options), TRUE);
+       gtk_window_set_keep_above (GTK_WINDOW(dialog_dumper_options), TRUE);
+       gtk_window_set_default_size (GTK_WINDOW(dialog_dumper_options), 180, -1);
+
+       table_options = gtk_table_new(3, 3, TRUE);
+       gtk_container_set_border_width (GTK_CONTAINER (table_options), 12);
+       gtk_table_set_row_spacings(GTK_TABLE(table_options), 5);
+       gtk_table_set_col_spacings(GTK_TABLE(table_options), 10);
+
+       pgui_create_options_dialog_add_label(table_options, _("Include gid column in the exported table"), 0.0, 0);
+       checkbutton_dumper_options_includegid = gtk_check_button_new();
+       align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
+       gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 0, 1 );
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_includegid);
+       
+       pgui_create_options_dialog_add_label(table_options, _("Preserve case of column names"), 0.0, 1);
+       checkbutton_dumper_options_keep_fieldname_case = gtk_check_button_new();
+       align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
+       gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 1, 2 );
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_keep_fieldname_case);
+
+       pgui_create_options_dialog_add_label(table_options, _("Escape column names"), 0.0, 2);
+       checkbutton_dumper_options_unescapedattrs = gtk_check_button_new();
+       align_options_center = gtk_alignment_new( 0.5, 0.5, 0.0, 1.0 );
+       gtk_table_attach_defaults(GTK_TABLE(table_options), align_options_center, 0, 1, 2, 3 );
+       gtk_container_add (GTK_CONTAINER (align_options_center), checkbutton_dumper_options_unescapedattrs);
+
+       /* Catch the response from the dialog */
+       g_signal_connect(dialog_dumper_options, "response", G_CALLBACK(pgui_action_dumper_options_close), dialog_dumper_options);
+       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_dumper_options)->vbox), table_options, FALSE, FALSE, 0);
+       
+       /* Hook the delete event so we don't destroy the dialog (just hide) if cancelled */
+       gtk_signal_connect(GTK_OBJECT(dialog_dumper_options), "delete_event", GTK_SIGNAL_FUNC(pgui_event_popup_delete), NULL);
 }
 
 /*
@@ -1870,21 +2745,121 @@ pgui_create_options_dialog()
  * up all the pretty signals.
  */
 static void
-pgui_create_file_table(GtkWidget *frame_shape)
+pgui_create_tablechooser_dialog()
+{
+       GtkWidget *vbox_tree, *table_progress;
+       GtkWidget *sw, *label;
+       GtkTreeSelection *chooser_selection;
+       gint *column_indexes;
+
+       /* Create the main top level window with a 10px border */
+       dialog_tablechooser = gtk_dialog_new_with_buttons(_("Table selection"), GTK_WINDOW(window_main),
+                               GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+                               
+       gtk_container_set_border_width(GTK_CONTAINER(dialog_tablechooser), 10);
+       gtk_window_set_position(GTK_WINDOW(dialog_tablechooser), GTK_WIN_POS_CENTER);
+       
+       vbox_tree = gtk_dialog_get_content_area(GTK_DIALOG(dialog_tablechooser));
+
+       /* Setup a model */
+       chooser_table_list_store = gtk_list_store_new(TABLECHOOSER_N_COLUMNS,
+                                        G_TYPE_STRING,
+                                        G_TYPE_STRING,
+                                        GTK_TYPE_TREE_MODEL,
+                                        G_TYPE_STRING,
+                                        G_TYPE_INT);
+       
+       /* Because we want to do selective filtering on the treeview content, we now implement a GtkTreeModel
+          filter on top of the original tree model */
+       chooser_filtered_table_list_store = (GtkListStore *)gtk_tree_model_filter_new(GTK_TREE_MODEL(chooser_table_list_store), NULL);
+       gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(chooser_filtered_table_list_store), 
+                                       (GtkTreeModelFilterVisibleFunc)table_chooser_visibility_func, NULL, NULL);
+                                        
+       /* Create the view and such */
+       chooser_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(chooser_filtered_table_list_store));
+       chooser_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(chooser_tree));
+       gtk_tree_selection_set_mode(chooser_selection, GTK_SELECTION_MULTIPLE);
+       
+       /* GTK has a slightly brain-dead API in that you can't directly find
+          the column being used by a GtkCellRenderer when using the same
+          callback to handle multiple fields; hence we manually store this
+          information here and pass a pointer to the column index into
+          the signal handler */
+       column_indexes = g_malloc(sizeof(gint) * TABLECHOOSER_N_COLUMNS);
+       
+       /* Make the tree view in a scrollable window */
+       sw = gtk_scrolled_window_new(NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
+       gtk_widget_set_size_request(sw, 320, 240);
+
+       gtk_box_pack_start(GTK_BOX(vbox_tree), sw, FALSE, FALSE, 10);
+       gtk_container_add(GTK_CONTAINER(sw), chooser_tree);
+       
+       /* Schema Field */
+       chooser_schema_renderer = gtk_cell_renderer_text_new();
+       g_object_set(chooser_schema_renderer, "editable", TRUE, NULL);
+       g_signal_connect(G_OBJECT(chooser_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), NULL);
+       chooser_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
+                       chooser_schema_renderer,
+                       "text",
+                       TABLECHOOSER_SCHEMA_COLUMN,
+                       NULL);
+       g_object_set(chooser_schema_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(chooser_tree), chooser_schema_column);
+
+       /* Table Field */
+       chooser_table_renderer = gtk_cell_renderer_text_new();
+       g_object_set(chooser_table_renderer, "editable", FALSE, NULL);
+       g_signal_connect(G_OBJECT(chooser_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), NULL);
+       chooser_table_column = gtk_tree_view_column_new_with_attributes("Table",
+                      chooser_table_renderer,
+                      "text",
+                      TABLECHOOSER_TABLE_COLUMN,
+                      NULL);
+       g_object_set(chooser_table_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(chooser_tree), chooser_table_column);
+       
+       /* Create table to hold the tick-box and text */
+       table_progress = gtk_table_new(1, 2, FALSE);
+       gtk_container_set_border_width (GTK_CONTAINER (table_progress), 0);
+       gtk_table_set_row_spacings(GTK_TABLE(table_progress), 0);
+       gtk_table_set_col_spacings(GTK_TABLE(table_progress), 0);
+       
+       checkbutton_chooser_geoonly = gtk_check_button_new();
+       gtk_table_attach(GTK_TABLE(table_progress), checkbutton_chooser_geoonly, 0, 1, 0, 1, GTK_SHRINK, GTK_FILL, 0, 0);
+       label = gtk_label_new(_("Only show tables with geo columns"));
+       gtk_table_attach(GTK_TABLE(table_progress), label, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 5, 0);
+       g_signal_connect(G_OBJECT(checkbutton_chooser_geoonly), "toggled", G_CALLBACK(pgui_action_chooser_toggle_show_geocolumn), NULL);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton_chooser_geoonly), TRUE);
+       
+       /* Attach table to the vbox */
+       gtk_box_pack_start(GTK_BOX(vbox_tree), table_progress, FALSE, FALSE, 10);
+               
+       return;
+}
+
+
+/*
+ * This function creates the UI artefacts for the file list table and hooks
+ * up all the pretty signals.
+ */
+static void
+pgui_create_import_file_table(GtkWidget *import_list_frame)
 {
        GtkWidget *vbox_tree;
        GtkWidget *sw;
        GtkTreeIter iter;
        gint *column_indexes;
        
-       gtk_container_set_border_width (GTK_CONTAINER (frame_shape), 0);
+       gtk_container_set_border_width (GTK_CONTAINER (import_list_frame), 0);
 
        vbox_tree = gtk_vbox_new(FALSE, 15);
        gtk_container_set_border_width(GTK_CONTAINER(vbox_tree), 5);
-       gtk_container_add(GTK_CONTAINER(frame_shape), vbox_tree);
+       gtk_container_add(GTK_CONTAINER(import_list_frame), vbox_tree);
 
        /* Setup a model */
-       list_store = gtk_list_store_new(N_COLUMNS,
+       import_file_list_store = gtk_list_store_new(IMPORT_N_COLUMNS,
                                         G_TYPE_POINTER,
                                         G_TYPE_STRING,
                                         G_TYPE_STRING,
@@ -1895,14 +2870,14 @@ pgui_create_file_table(GtkWidget *frame_shape)
                                         G_TYPE_BOOLEAN);
        
        /* Create the view and such */
-       tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
+       import_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(import_file_list_store));
        
        /* GTK has a slightly brain-dead API in that you can't directly find
           the column being used by a GtkCellRenderer when using the same
           callback to handle multiple fields; hence we manually store this
           information here and pass a pointer to the column index into
           the signal handler */
-       column_indexes = g_malloc(sizeof(gint) * N_COLUMNS);
+       column_indexes = g_malloc(sizeof(gint) * IMPORT_N_COLUMNS);
        
        /* Make the tree view in a scrollable window */
        sw = gtk_scrolled_window_new(NULL, NULL);
@@ -1911,132 +2886,133 @@ pgui_create_file_table(GtkWidget *frame_shape)
        gtk_widget_set_size_request(sw, -1, 150);
        
        gtk_box_pack_start(GTK_BOX(vbox_tree), sw, TRUE, TRUE, 0);
-       gtk_container_add(GTK_CONTAINER (sw), tree);
+       gtk_container_add(GTK_CONTAINER (sw), import_tree);
 
        /* Place the "Add File" button below the list view */
        add_file_button = gtk_button_new_with_label(_("Add File"));
        gtk_container_add (GTK_CONTAINER (vbox_tree), add_file_button);
        
        /* Filename Field */
-       filename_renderer = gtk_cell_renderer_text_new();
-       g_object_set(filename_renderer, "editable", FALSE, NULL);
-       column_indexes[FILENAME_COLUMN] = FILENAME_COLUMN;
-       g_signal_connect(G_OBJECT(filename_renderer), "edited", G_CALLBACK(pgui_action_handle_tree_edit), &column_indexes[FILENAME_COLUMN]);
-       filename_column = gtk_tree_view_column_new_with_attributes(_("Shapefile"),
-                         filename_renderer,
+       import_filename_renderer = gtk_cell_renderer_text_new();
+       g_object_set(import_filename_renderer, "editable", FALSE, NULL);
+       column_indexes[IMPORT_FILENAME_COLUMN] = IMPORT_FILENAME_COLUMN;
+       g_signal_connect(G_OBJECT(import_filename_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_FILENAME_COLUMN]);
+       import_filename_column = gtk_tree_view_column_new_with_attributes(_("Shapefile"),
+                         import_filename_renderer,
                          "text",
-                         FILENAME_COLUMN,
+                         IMPORT_FILENAME_COLUMN,
                          NULL);
-       g_object_set(filename_column, "resizable", TRUE, NULL);
-       gtk_tree_view_append_column(GTK_TREE_VIEW(tree), filename_column);
+       g_object_set(import_filename_column, "resizable", TRUE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_filename_column);
 
        /* Schema Field */
-       schema_renderer = gtk_cell_renderer_text_new();
-       g_object_set(schema_renderer, "editable", TRUE, NULL);
-       column_indexes[SCHEMA_COLUMN] = SCHEMA_COLUMN;
-       g_signal_connect(G_OBJECT(schema_renderer), "edited", G_CALLBACK(pgui_action_handle_tree_edit), &column_indexes[SCHEMA_COLUMN]);
-       schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
-                       schema_renderer,
+       import_schema_renderer = gtk_cell_renderer_text_new();
+       g_object_set(import_schema_renderer, "editable", TRUE, NULL);
+       column_indexes[IMPORT_SCHEMA_COLUMN] = IMPORT_SCHEMA_COLUMN;
+       g_signal_connect(G_OBJECT(import_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_SCHEMA_COLUMN]);
+       import_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
+                       import_schema_renderer,
                        "text",
-                       SCHEMA_COLUMN,
+                       IMPORT_SCHEMA_COLUMN,
                        NULL);
-       g_object_set(schema_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
-       gtk_tree_view_append_column(GTK_TREE_VIEW(tree), schema_column);
+       g_object_set(import_schema_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_schema_column);
 
        /* Table Field */
-       table_renderer = gtk_cell_renderer_text_new();
-       g_object_set(table_renderer, "editable", TRUE, NULL);
-       column_indexes[TABLE_COLUMN] = TABLE_COLUMN;
-       g_signal_connect(G_OBJECT(table_renderer), "edited", G_CALLBACK(pgui_action_handle_tree_edit), &column_indexes[TABLE_COLUMN]);
-       table_column = gtk_tree_view_column_new_with_attributes("Table",
-                      table_renderer,
+       import_table_renderer = gtk_cell_renderer_text_new();
+       g_object_set(import_table_renderer, "editable", TRUE, NULL);
+       column_indexes[IMPORT_TABLE_COLUMN] = IMPORT_TABLE_COLUMN;
+       g_signal_connect(G_OBJECT(import_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_TABLE_COLUMN]);
+       import_table_column = gtk_tree_view_column_new_with_attributes("Table",
+                      import_table_renderer,
                       "text",
-                      TABLE_COLUMN,
+                      IMPORT_TABLE_COLUMN,
                       NULL);
-       g_object_set(schema_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
-       gtk_tree_view_append_column(GTK_TREE_VIEW(tree), table_column);
+       g_object_set(import_schema_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_table_column);
 
        /* Geo column field */
-       geom_column_renderer = gtk_cell_renderer_text_new();
-       g_object_set(geom_column_renderer, "editable", TRUE, NULL);
-       column_indexes[GEOMETRY_COLUMN] = GEOMETRY_COLUMN;
-       g_signal_connect(G_OBJECT(geom_column_renderer), "edited", G_CALLBACK(pgui_action_handle_tree_edit), &column_indexes[GEOMETRY_COLUMN]);
-       geom_column = gtk_tree_view_column_new_with_attributes(_("Geo Column"),
-                     geom_column_renderer,
+       import_geom_column_renderer = gtk_cell_renderer_text_new();
+       g_object_set(import_geom_column_renderer, "editable", TRUE, NULL);
+       column_indexes[IMPORT_GEOMETRY_COLUMN] = IMPORT_GEOMETRY_COLUMN;
+       g_signal_connect(G_OBJECT(import_geom_column_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_GEOMETRY_COLUMN]);
+       import_geom_column = gtk_tree_view_column_new_with_attributes(_("Geo Column"),
+                     import_geom_column_renderer,
                      "text",
-                     GEOMETRY_COLUMN,
+                     IMPORT_GEOMETRY_COLUMN,
                      NULL);
-       g_object_set(geom_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
-       gtk_tree_view_append_column(GTK_TREE_VIEW(tree), geom_column);
+       g_object_set(import_geom_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_geom_column);
 
        /* SRID Field */
-       srid_renderer = gtk_cell_renderer_text_new();
-       g_object_set(srid_renderer, "editable", TRUE, NULL);
-       column_indexes[SRID_COLUMN] = SRID_COLUMN;
-       g_signal_connect(G_OBJECT(srid_renderer), "edited", G_CALLBACK(pgui_action_handle_tree_edit), &column_indexes[SRID_COLUMN]);
-       srid_column = gtk_tree_view_column_new_with_attributes("SRID",
-                     srid_renderer,
+       import_srid_renderer = gtk_cell_renderer_text_new();
+       g_object_set(import_srid_renderer, "editable", TRUE, NULL);
+       column_indexes[IMPORT_SRID_COLUMN] = IMPORT_SRID_COLUMN;
+       g_signal_connect(G_OBJECT(import_srid_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[IMPORT_SRID_COLUMN]);
+       import_srid_column = gtk_tree_view_column_new_with_attributes("SRID",
+                     import_srid_renderer,
                      "text",
-                     SRID_COLUMN,
+                     IMPORT_SRID_COLUMN,
                      NULL);
-       g_object_set(srid_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);  
-       gtk_tree_view_append_column(GTK_TREE_VIEW(tree), srid_column);
+       g_object_set(import_srid_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_srid_column);
 
        /* Mode Combo Field */
-       combo_list = gtk_list_store_new(COMBO_COLUMNS, 
+       loader_mode_combo_list = gtk_list_store_new(LOADER_MODE_COMBO_COLUMNS, 
                                        G_TYPE_STRING,
                                        G_TYPE_CHAR);
        
-       gtk_list_store_insert(combo_list, &iter, CREATE_MODE);
-       gtk_list_store_set(combo_list, &iter,
-                          COMBO_TEXT, _("Create"), 
-                          COMBO_OPTION_CHAR, 'c',                         
+       gtk_list_store_insert(loader_mode_combo_list, &iter, CREATE_MODE);
+       gtk_list_store_set(loader_mode_combo_list, &iter,
+                          LOADER_MODE_COMBO_TEXT, _("Create"), 
+                          LOADER_MODE_COMBO_OPTION_CHAR, 'c',                     
                           -1);
-       gtk_list_store_insert(combo_list, &iter, APPEND_MODE);
-       gtk_list_store_set(combo_list, &iter,
-                          COMBO_TEXT, _("Append"), 
-                          COMBO_OPTION_CHAR, 'a', 
+       gtk_list_store_insert(loader_mode_combo_list, &iter, APPEND_MODE);
+       gtk_list_store_set(loader_mode_combo_list, &iter,
+                          LOADER_MODE_COMBO_TEXT, _("Append"), 
+                          LOADER_MODE_COMBO_OPTION_CHAR, 'a', 
                           -1);
-       gtk_list_store_insert(combo_list, &iter, DELETE_MODE);
-       gtk_list_store_set(combo_list, &iter,
-                          COMBO_TEXT, _("Delete"), 
-                          COMBO_OPTION_CHAR, 'd', 
+       gtk_list_store_insert(loader_mode_combo_list, &iter, DELETE_MODE);
+       gtk_list_store_set(loader_mode_combo_list, &iter,
+                          LOADER_MODE_COMBO_TEXT, _("Delete"), 
+                          LOADER_MODE_COMBO_OPTION_CHAR, 'd', 
                           -1);
-       gtk_list_store_insert(combo_list, &iter, PREPARE_MODE);
-       gtk_list_store_set(combo_list, &iter,
-                          COMBO_TEXT, _("Prepare"), 
-                          COMBO_OPTION_CHAR, 'p', 
+       gtk_list_store_insert(loader_mode_combo_list, &iter, PREPARE_MODE);
+       gtk_list_store_set(loader_mode_combo_list, &iter,
+                          LOADER_MODE_COMBO_TEXT, _("Prepare"), 
+                          LOADER_MODE_COMBO_OPTION_CHAR, 'p', 
                           -1);
-       mode_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(combo_list));
-       mode_renderer = gtk_cell_renderer_combo_new();
-       gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(mode_combo),
-                                  mode_renderer, TRUE);
-       gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(mode_combo),
-                                     mode_renderer, "text", 0);
-       g_object_set(mode_renderer,
-                    "model", combo_list,
+       loader_mode_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(loader_mode_combo_list));
+       import_mode_renderer = gtk_cell_renderer_combo_new();
+       gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(loader_mode_combo),
+                                  import_mode_renderer, TRUE);
+       gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(loader_mode_combo),
+                                     import_mode_renderer, "text", 0);
+       g_object_set(import_mode_renderer,
+                    "model", loader_mode_combo_list,
                     "editable", TRUE,
                     "has-entry", FALSE,
-                    "text-column", COMBO_TEXT,
+                    "text-column", LOADER_MODE_COMBO_TEXT,
                     NULL);
-       mode_column = gtk_tree_view_column_new_with_attributes(_("Mode"),
-                     mode_renderer,
+       import_mode_column = gtk_tree_view_column_new_with_attributes(_("Mode"),
+                     import_mode_renderer,
                      "text",
-                     MODE_COLUMN,
+                     IMPORT_MODE_COLUMN,
                      NULL);
-       gtk_tree_view_append_column(GTK_TREE_VIEW(tree), mode_column);
-       gtk_combo_box_set_active(GTK_COMBO_BOX(mode_combo), 1);
-
-       g_signal_connect (G_OBJECT(mode_renderer), "changed", G_CALLBACK(pgui_action_handle_tree_combo), NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_mode_column);
+       gtk_combo_box_set_active(GTK_COMBO_BOX(loader_mode_combo), 1);
+       g_object_set(import_mode_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       
+       g_signal_connect (G_OBJECT(import_mode_renderer), "changed", G_CALLBACK(pgui_action_handle_tree_combo), NULL);
 
        /* Remove Field */
-       remove_renderer = gtk_cell_renderer_toggle_new();
-       g_object_set(remove_renderer, "activatable", TRUE, NULL);
-       g_signal_connect(G_OBJECT(remove_renderer), "toggled", G_CALLBACK (pgui_action_handle_tree_remove), NULL);
-       remove_column = gtk_tree_view_column_new_with_attributes("Rm",
-                       remove_renderer, NULL);
-       g_object_set(remove_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
-       gtk_tree_view_append_column(GTK_TREE_VIEW(tree), remove_column);
+       import_remove_renderer = gtk_cell_renderer_toggle_new();
+       g_object_set(import_remove_renderer, "activatable", TRUE, NULL);
+       g_signal_connect(G_OBJECT(import_remove_renderer), "toggled", G_CALLBACK (pgui_action_handle_file_remove), NULL);
+       import_remove_column = gtk_tree_view_column_new_with_attributes("Rm",
+                       import_remove_renderer, NULL);
+       g_object_set(import_remove_column, "resizable", TRUE, "expand", FALSE, "fixed-width", 64, "sizing", GTK_TREE_VIEW_COLUMN_FIXED, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(import_tree), import_remove_column);
 
        g_signal_connect (G_OBJECT (add_file_button), "clicked", G_CALLBACK (pgui_action_open_file_dialog), NULL);
 
@@ -2047,14 +3023,136 @@ pgui_create_file_table(GtkWidget *frame_shape)
        };
        
        gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
-       gtk_drag_dest_set(GTK_WIDGET(tree),
+       gtk_drag_dest_set(GTK_WIDGET(import_tree),
                          GTK_DEST_DEFAULT_ALL,
                          drop_types, n_drop_types,
                          GDK_ACTION_COPY);
-       g_signal_connect(G_OBJECT(tree), "drag_data_received",
+       g_signal_connect(G_OBJECT(import_tree), "drag_data_received",
                         G_CALLBACK(pgui_action_handle_file_drop), NULL);
 }
 
+/*
+ * This function creates the UI artefacts for the file list table and hooks
+ * up all the pretty signals.
+ */
+static void
+pgui_create_export_table_table(GtkWidget *export_list_frame)
+{
+       GtkWidget *vbox_tree;
+       GtkWidget *sw;
+       gint *column_indexes;
+       
+       gtk_container_set_border_width (GTK_CONTAINER (export_list_frame), 0);
+
+       vbox_tree = gtk_vbox_new(FALSE, 15);
+       gtk_container_set_border_width(GTK_CONTAINER(vbox_tree), 5);
+       gtk_container_add(GTK_CONTAINER(export_list_frame), vbox_tree);
+
+       /* Setup a model */
+       export_table_list_store = gtk_list_store_new(EXPORT_N_COLUMNS,
+                                        G_TYPE_POINTER,
+                                        G_TYPE_STRING,
+                                        G_TYPE_STRING,
+                                        G_TYPE_STRING,
+                                        GTK_TYPE_TREE_MODEL,
+                                        G_TYPE_STRING,
+                                        G_TYPE_BOOLEAN);
+       
+       /* Create the view and such */
+       export_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(export_table_list_store));
+       
+       /* GTK has a slightly brain-dead API in that you can't directly find
+          the column being used by a GtkCellRenderer when using the same
+          callback to handle multiple fields; hence we manually store this
+          information here and pass a pointer to the column index into
+          the signal handler */
+       column_indexes = g_malloc(sizeof(gint) * EXPORT_N_COLUMNS);
+       
+       /* Make the tree view in a scrollable window */
+       sw = gtk_scrolled_window_new(NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
+       gtk_widget_set_size_request(sw, -1, 150);
+       
+       gtk_box_pack_start(GTK_BOX(vbox_tree), sw, TRUE, TRUE, 0);
+       gtk_container_add(GTK_CONTAINER (sw), export_tree);
+
+       /* Place the "Add Table" button below the list view */
+       add_table_button = gtk_button_new_with_label(_("Add Table"));
+       gtk_container_add (GTK_CONTAINER (vbox_tree), add_table_button);
+
+       /* Schema Field */
+       export_schema_renderer = gtk_cell_renderer_text_new();
+       g_object_set(export_schema_renderer, "editable", TRUE, NULL);
+       column_indexes[EXPORT_SCHEMA_COLUMN] = EXPORT_SCHEMA_COLUMN;
+       g_signal_connect(G_OBJECT(import_schema_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[EXPORT_SCHEMA_COLUMN]);
+       export_schema_column = gtk_tree_view_column_new_with_attributes(_("Schema"),
+                       export_schema_renderer,
+                       "text",
+                       EXPORT_SCHEMA_COLUMN,
+                       NULL);
+       g_object_set(export_schema_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_schema_column);
+
+       /* Table Field */
+       export_table_renderer = gtk_cell_renderer_text_new();
+       g_object_set(export_table_renderer, "editable", FALSE, NULL);
+       column_indexes[EXPORT_TABLE_COLUMN] = EXPORT_TABLE_COLUMN;
+       g_signal_connect(G_OBJECT(export_table_renderer), "edited", G_CALLBACK(pgui_action_handle_loader_edit), &column_indexes[EXPORT_TABLE_COLUMN]);
+       export_table_column = gtk_tree_view_column_new_with_attributes("Table",
+                      export_table_renderer,
+                      "text",
+                      EXPORT_TABLE_COLUMN,
+                      NULL);
+       g_object_set(export_table_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_table_column);
+
+       /* Geo column field */
+       export_geom_column_combo = gtk_combo_box_new();
+       export_geom_column_renderer = gtk_cell_renderer_combo_new();
+       gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(export_geom_column_combo),
+                                  export_geom_column_renderer, TRUE);
+       g_object_set(export_geom_column_renderer, 
+                    "editable", TRUE,
+                    "has-entry", FALSE,
+                    "text-column", TABLECHOOSER_GEOCOL_COMBO_TEXT, 
+                    NULL);
+       export_geom_column = gtk_tree_view_column_new_with_attributes("Geo Column",
+                      export_geom_column_renderer,
+                      "model",
+                      EXPORT_GEOMETRY_LISTSTORE_COLUMN,
+                      "text",
+                      EXPORT_GEOMETRY_COLUMN,
+                      NULL);
+       g_object_set(export_geom_column, "resizable", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_geom_column);
+       g_signal_connect (G_OBJECT(export_geom_column_renderer), "changed", G_CALLBACK(pgui_action_handle_table_geocol_combo), NULL);
+
+       /* Filename Field */
+       export_filename_renderer = gtk_cell_renderer_text_new();
+       g_object_set(export_filename_renderer, "editable", TRUE, NULL);
+       column_indexes[EXPORT_FILENAME_COLUMN] = EXPORT_FILENAME_COLUMN;
+       g_signal_connect(G_OBJECT(export_filename_renderer), "edited", G_CALLBACK(pgui_action_handle_dumper_edit), &column_indexes[EXPORT_FILENAME_COLUMN]);
+       export_filename_column = gtk_tree_view_column_new_with_attributes("Filename",
+                      export_filename_renderer,
+                      "text",
+                      EXPORT_FILENAME_COLUMN,
+                      NULL);
+       g_object_set(export_filename_column, "resizable", TRUE, "expand", TRUE, "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_filename_column);
+       
+       /* Remove Field */
+       export_remove_renderer = gtk_cell_renderer_toggle_new();
+       g_object_set(export_remove_renderer, "activatable", TRUE, NULL);
+       g_signal_connect(G_OBJECT(export_remove_renderer), "toggled", G_CALLBACK (pgui_action_handle_table_remove), NULL);
+       export_remove_column = gtk_tree_view_column_new_with_attributes("Rm",
+                       export_remove_renderer, NULL);
+       g_object_set(export_remove_column, "resizable", TRUE, "expand", FALSE, "fixed-width", 64, "sizing", GTK_TREE_VIEW_COLUMN_FIXED, NULL);
+       gtk_tree_view_append_column(GTK_TREE_VIEW(export_tree), export_remove_column);
+
+       g_signal_connect (G_OBJECT (add_table_button), "clicked", G_CALLBACK (pgui_action_open_table_dialog), NULL);
+}
+
 static void
 pgui_create_connection_window()
 {
@@ -2146,17 +3244,18 @@ static void
 pgui_create_main_window(const SHPCONNECTIONCONFIG *conn)
 {
        /* Main widgets */
-       GtkWidget *vbox_main, *vbox_loader;
+       GtkWidget *vbox_main, *vbox_loader, *vbox_dumper;
        
        /* PgSQL section */
-       GtkWidget *frame_pg, *frame_shape, *frame_log;
+       GtkWidget *frame_pg, *import_list_frame, *export_list_frame, *frame_log;
        GtkWidget *button_pg_conn;
        
        /* Notebook */
        GtkWidget *notebook;
        
        /* Button section */
-       GtkWidget *hbox_buttons, *button_options, *button_import, *button_cancel, *button_about;
+       GtkWidget *loader_hbox_buttons, *loader_button_options, *loader_button_import, *loader_button_cancel, *loader_button_about;
+       GtkWidget *dumper_hbox_buttons, *dumper_button_options, *dumper_button_export, *dumper_button_cancel, *dumper_button_about;
        
        /* Log section */
        GtkWidget *scrolledwindow_log;
@@ -2194,30 +3293,63 @@ pgui_create_main_window(const SHPCONNECTIONCONFIG *conn)
        /*
        ** Shape file selector
        */
-       frame_shape = gtk_frame_new(_("Import List"));
-       pgui_create_file_table(frame_shape);
+       import_list_frame = gtk_frame_new(_("Import List"));
+       pgui_create_import_file_table(import_list_frame);
 
        /*
        ** Row of action buttons
        */
-       hbox_buttons = gtk_hbox_new(TRUE, 15);
-       gtk_container_set_border_width (GTK_CONTAINER (hbox_buttons), 0);
+       loader_hbox_buttons = gtk_hbox_new(TRUE, 15);
+       gtk_container_set_border_width (GTK_CONTAINER (loader_hbox_buttons), 0);
+
        /* Create the buttons themselves */
-       button_options = gtk_button_new_with_label(_("Options..."));
-       button_import = gtk_button_new_with_label(_("Import"));
-       button_cancel = gtk_button_new_with_label(_("Cancel"));
-       button_about = gtk_button_new_with_label(_("About"));
+       loader_button_options = gtk_button_new_with_label(_("Options..."));
+       loader_button_import = gtk_button_new_with_label(_("Import"));
+       loader_button_cancel = gtk_button_new_with_label(_("Cancel"));
+       loader_button_about = gtk_button_new_with_label(_("About"));
+
        /* Add actions to the buttons */
-       g_signal_connect (G_OBJECT (button_import), "clicked", G_CALLBACK (pgui_action_import), NULL);
-       g_signal_connect (G_OBJECT (button_options), "clicked", G_CALLBACK (pgui_action_options_open), NULL);
-       g_signal_connect (G_OBJECT (button_cancel), "clicked", G_CALLBACK (pgui_action_cancel), NULL);
-       g_signal_connect (G_OBJECT (button_about), "clicked", G_CALLBACK (pgui_action_about_open), NULL);
+       g_signal_connect (G_OBJECT (loader_button_import), "clicked", G_CALLBACK (pgui_action_import), NULL);
+       g_signal_connect (G_OBJECT (loader_button_options), "clicked", G_CALLBACK (pgui_action_loader_options_open), NULL);
+       g_signal_connect (G_OBJECT (loader_button_cancel), "clicked", G_CALLBACK (pgui_action_cancel), NULL);
+       g_signal_connect (G_OBJECT (loader_button_about), "clicked", G_CALLBACK (pgui_action_about_open), NULL);
+
        /* And insert the buttons into the hbox */
-       gtk_box_pack_start(GTK_BOX(hbox_buttons), button_options, TRUE, TRUE, 0);
-       gtk_box_pack_end(GTK_BOX(hbox_buttons), button_cancel, TRUE, TRUE, 0);
-       gtk_box_pack_end(GTK_BOX(hbox_buttons), button_about, TRUE, TRUE, 0);
-       gtk_box_pack_end(GTK_BOX(hbox_buttons), button_import, TRUE, TRUE, 0);
+       gtk_box_pack_start(GTK_BOX(loader_hbox_buttons), loader_button_options, TRUE, TRUE, 0);
+       gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_cancel, TRUE, TRUE, 0);
+       gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_about, TRUE, TRUE, 0);
+       gtk_box_pack_end(GTK_BOX(loader_hbox_buttons), loader_button_import, TRUE, TRUE, 0);
+
+       /*
+       ** Table selector
+       */
+       export_list_frame = gtk_frame_new(_("Export List"));
+       pgui_create_export_table_table(export_list_frame);
+
+       /*
+       ** Row of action buttons
+       */
+       dumper_hbox_buttons = gtk_hbox_new(TRUE, 15);
+       gtk_container_set_border_width (GTK_CONTAINER (dumper_hbox_buttons), 0);
+
+       /* Create the buttons themselves */
+       dumper_button_options = gtk_button_new_with_label(_("Options..."));
+       dumper_button_export = gtk_button_new_with_label(_("Export"));
+       dumper_button_cancel = gtk_button_new_with_label(_("Cancel"));
+       dumper_button_about = gtk_button_new_with_label(_("About"));
+       
+       /* Add actions to the buttons */
+       g_signal_connect (G_OBJECT (dumper_button_export), "clicked", G_CALLBACK (pgui_action_export), NULL);
+       g_signal_connect (G_OBJECT (dumper_button_options), "clicked", G_CALLBACK (pgui_action_dumper_options_open), NULL);
+       g_signal_connect (G_OBJECT (dumper_button_cancel), "clicked", G_CALLBACK (pgui_action_cancel), NULL);
+       g_signal_connect (G_OBJECT (dumper_button_about), "clicked", G_CALLBACK (pgui_action_about_open), NULL);
 
+       /* And insert the buttons into the hbox */
+       gtk_box_pack_start(GTK_BOX(dumper_hbox_buttons), dumper_button_options, TRUE, TRUE, 0);
+       gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_cancel, TRUE, TRUE, 0);
+       gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_about, TRUE, TRUE, 0);
+       gtk_box_pack_end(GTK_BOX(dumper_hbox_buttons), dumper_button_export, TRUE, TRUE, 0);
+       
        /*
        ** Log window
        */
@@ -2246,10 +3378,18 @@ pgui_create_main_window(const SHPCONNECTIONCONFIG *conn)
        vbox_loader = gtk_vbox_new(FALSE, 10);
        gtk_container_set_border_width(GTK_CONTAINER(vbox_loader), 10);
        
-       gtk_box_pack_start(GTK_BOX(vbox_loader), frame_shape, FALSE, TRUE, 0);
-       gtk_box_pack_start(GTK_BOX(vbox_loader), hbox_buttons, FALSE, FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(vbox_loader), import_list_frame, FALSE, TRUE, 0);
+       gtk_box_pack_start(GTK_BOX(vbox_loader), loader_hbox_buttons, FALSE, FALSE, 0);
        gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_loader, gtk_label_new(_("Import")));
        
+       /* Add the dumper frames into the notebook page */
+       vbox_dumper = gtk_vbox_new(FALSE, 10);
+       gtk_container_set_border_width(GTK_CONTAINER(vbox_dumper), 10);
+       
+       gtk_box_pack_start(GTK_BOX(vbox_dumper), export_list_frame, FALSE, TRUE, 0);
+       gtk_box_pack_start(GTK_BOX(vbox_dumper), dumper_hbox_buttons, FALSE, FALSE, 0);
+       gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox_dumper, gtk_label_new(_("Export")));      
+       
        /* Add the frames into the main vbox */
        gtk_box_pack_start(GTK_BOX(vbox_main), frame_pg, FALSE, TRUE, 0);
        gtk_box_pack_start(GTK_BOX(vbox_main), notebook, FALSE, TRUE, 0);
@@ -2292,7 +3432,9 @@ main(int argc, char *argv[])
        /* Parse command line options and set configuration */
        global_loader_config = malloc(sizeof(SHPLOADERCONFIG));
        set_loader_config_defaults(global_loader_config);
-
+       global_dumper_config = malloc(sizeof(SHPDUMPERCONFIG));
+       set_dumper_config_defaults(global_dumper_config);
+       
        /* Here we override any defaults for the GUI */
        global_loader_config->createindex = 1;
        global_loader_config->geo_col = strdup(GEOMETRY_DEFAULT);
@@ -2338,11 +3480,14 @@ main(int argc, char *argv[])
        /* set up the user interface */
        pgui_create_main_window(conn);
        pgui_create_connection_window();
-       pgui_create_options_dialog();
+       pgui_create_loader_options_dialog();
+       pgui_create_dumper_options_dialog();
        pgui_create_about_dialog();
        pgui_create_filechooser_dialog();
        pgui_create_progress_dialog();
-
+       pgui_create_tablechooser_dialog();
+       pgui_create_folderchooser_dialog();
+       
        /* start the main loop */
        gtk_main();