Commit a42618c0 authored by Murray Cumming's avatar Murray Cumming Committed by Murray Cumming
Browse files

Add a File/Export menu item, for exporting the found set as

2006-01-14  Murray Cumming  <murrayc@murrayc.com>

        * glom/frame_glom.cc:
        * glom/frame_glom.h: Add a File/Export menu item, for exporting the found set as
        comma-separated text.
        * glom/base_db.cc:
        * glom/base_db.h:
        * glom/box_db_table.cc:
        * glom/box_db_table.h:
        * glom/data_structure/glomconversions.cc:
        * glom/data_structure/layout/layoutitem_field.h:
        * glom/filechooser_export.cc:
        * glom/filechooser_export.h:
        * glom/mode_data/Makefile.am:
        * glom/mode_data/box_data.cc:
        * glom/mode_data/box_data.h:
        * glom/mode_data/box_data_details.cc:
        * glom/mode_data/box_data_list.cc:
        * glom/mode_data/box_data_list_related.cc:
        * glom/mode_data/notebook_data.cc:
        * glom/mode_data/notebook_data.h:
        * glom/base_db.cc:
        * glom/base_db.h:Moved various folders into Base_DB so that Frame_Glom can use them.
        * glom/Makefile.am:
        * glom/mode_data/dialog_layout_export.cc:
        * glom/mode_data/dialog_layout_export.h: New layout UI for export field sequences,
        not yet finished or used.
parent 254c2e49
2006-01-14 Murray Cumming <murrayc@murrayc.com>
* glom/frame_glom.cc:
* glom/frame_glom.h: Add a File/Export menu item, for exporting the found set as
comma-separated text.
* glom/base_db.cc:
* glom/base_db.h:
* glom/box_db_table.cc:
* glom/box_db_table.h:
* glom/data_structure/glomconversions.cc:
* glom/data_structure/layout/layoutitem_field.h:
* glom/filechooser_export.cc:
* glom/filechooser_export.h:
* glom/mode_data/Makefile.am:
* glom/mode_data/box_data.cc:
* glom/mode_data/box_data.h:
* glom/mode_data/box_data_details.cc:
* glom/mode_data/box_data_list.cc:
* glom/mode_data/box_data_list_related.cc:
* glom/mode_data/notebook_data.cc:
* glom/mode_data/notebook_data.h:
* glom/base_db.cc:
* glom/base_db.h:Moved various folders into Base_DB so that Frame_Glom can use them.
* glom/Makefile.am:
* glom/mode_data/dialog_layout_export.cc:
* glom/mode_data/dialog_layout_export.h: New layout UI for export field sequences,
not yet finished or used.
2006-01-13 Murray Cumming <murrayc@murrayc.com>
* glom/frame_glom.cc:
......
......@@ -35,6 +35,7 @@ glom_SOURCES = main.cc \
dialog_new_database.h dialog_new_database.cc \
dialog_database_preferences.h dialog_database_preferences.cc \
dialog_layout_report.h dialog_layout_report.cc \
filechooser_export.h filechooser_export.cc \
sharedptr.h \
standard_table_prefs_fields.h \
box_reports.h box_reports.cc \
......
......@@ -132,7 +132,7 @@ void App_Glom::init_menus_file()
{
//Overridden to remove the Save and Save-As menu items,
//because all changes are saved immediately and automatically.
// File menu
//Build actions:
......@@ -147,6 +147,9 @@ void App_Glom::init_menus_file()
m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_Open", Gtk::Stock::OPEN),
sigc::mem_fun((App_WithDoc&)*this, &App_WithDoc::on_menu_file_open));
m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File_Export", _("_Export")),
sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_file_export));
m_refFileActionGroup->add(Gtk::Action::create("GlomAction_File_Print", Gtk::Stock::PRINT),
sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_file_print) );
......@@ -167,6 +170,7 @@ void App_Glom::init_menus_file()
" <menuitem action='BakeryAction_File_Open' />"
" <menu action='BakeryAction_Menu_File_RecentFiles'>"
" </menu>"
" <menuitem action='BakeryAction_Menu_File_Export' />"
" <separator/>"
" <menuitem action='GlomAction_File_Print' />"
" <separator/>"
......
......@@ -1594,5 +1594,251 @@ Glib::ustring Base_DB::get_find_where_clause_quick(const Glib::ustring& table_na
return strClause;
}
bool Base_DB::get_fields_for_table_one_field(const Glib::ustring& table_name, const Glib::ustring& field_name, Field& field) const
{
//Initialize output parameter:
Field result = Field();
type_vecFields fields = get_fields_for_table(table_name);
type_vecFields::iterator iter = std::find_if(fields.begin(), fields.end(), predicate_FieldHasName<Field>(field_name));
if(iter != fields.end()) //TODO: Handle error?
{
field = *iter;
return true;
}
return false;
}
//static:
bool Base_DB::get_field_primary_key_index_for_fields(const type_vecFields& fields, guint& field_column)
{
//Initialize input parameter:
field_column = 0;
//TODO_performance: Cache the primary key?
guint col = 0;
guint cols_count = fields.size();
while(col < cols_count)
{
if(fields[col].get_primary_key())
{
field_column = col;
return true;
}
else
{
++col;
}
}
return false; //Not found.
}
//static:
bool Base_DB::get_field_primary_key_index_for_fields(const type_vecLayoutFields& fields, guint& field_column)
{
//Initialize input parameter:
field_column = 0;
//TODO_performance: Cache the primary key?
guint col = 0;
guint cols_count = fields.size();
while(col < cols_count)
{
if(fields[col]->m_field.get_primary_key())
{
field_column = col;
return true;
}
else
{
++col;
}
}
return false; //Not found.
}
bool Base_DB::get_field_primary_key_for_table(const Glib::ustring& table_name, Field& field) const
{
const Document_Glom* document = get_document();
if(document)
{
//TODO_Performance:
Document_Glom::type_vecFields fields = document->get_table_fields(table_name);
for(Document_Glom::type_vecFields::iterator iter = fields.begin(); iter != fields.end(); ++iter)
{
if(iter->get_primary_key())
{
field = *iter;
return true;
}
}
}
return false;
}
void Base_DB::get_table_fields_to_show_for_sequence_add_group(const Glib::ustring& table_name, const Privileges& table_privs, const type_vecFields& all_db_fields, const LayoutGroup& group, Base_DB::type_vecLayoutFields& vecFields) const
{
//g_warning("Box_Data::get_table_fields_to_show_for_sequence_add_group(): table_name=%s, all_db_fields.size()=%d, group.name=%s", table_name.c_str(), all_db_fields.size(), group.get_name().c_str());
const Document_Glom* document = get_document();
LayoutGroup::type_map_const_items items = group.get_items();
for(LayoutGroup::type_map_const_items::const_iterator iterItems = items.begin(); iterItems != items.end(); ++iterItems)
{
const LayoutItem* item = iterItems->second;
const LayoutItem_Field* item_field = dynamic_cast<const LayoutItem_Field*>(item);
if(item_field)
{
//Get the field info:
const Glib::ustring field_name = item->get_name();
if(item_field->get_has_relationship_name()) //If it's a field in a related table.
{
//Get the full field information:
const Glib::ustring relationship_name = item_field->get_relationship_name();
Relationship relationship;
bool test = document->get_relationship(table_name, relationship_name, relationship);
if(test)
{
Field field;
//TODO_Performance: get_fields_for_table_one_field() is probably very inefficient
bool test = get_fields_for_table_one_field(relationship.get_to_table(), item->get_name(), field);
if(test)
{
LayoutItem_Field layout_item = *item_field; //TODO_Performance: Reduce the copying.
layout_item.m_field = field; //Fill in the full field information for later.
//TODO_Performance: We do this once for each related field, even if there are 2 from the same table:
const Privileges privs_related = get_current_privs(relationship.get_to_table());
layout_item.m_priv_view = privs_related.m_view;
layout_item.m_priv_edit = privs_related.m_edit;
vecFields.push_back( sharedptr<LayoutItem_Field>(new LayoutItem_Field(layout_item)) );
}
}
}
else //It's a regular field in the table:
{
type_vecFields::const_iterator iterFind = std::find_if(all_db_fields.begin(), all_db_fields.end(), predicate_FieldHasName<Field>(field_name));
//If the field does not exist anymore then we won't try to show it:
if(iterFind != all_db_fields.end() )
{
LayoutItem_Field layout_item = *item_field; //TODO_Performance: Reduce the copying here.
layout_item.m_field = *iterFind; //Fill the LayoutItem with the full field information.
//Prevent editing of the field if the user may not edit this table:
layout_item.m_priv_view = table_privs.m_view;
layout_item.m_priv_edit = table_privs.m_edit;
vecFields.push_back( sharedptr<LayoutItem_Field>(new LayoutItem_Field(layout_item)) );
}
}
}
else
{
const LayoutGroup* item_group = dynamic_cast<const LayoutGroup*>(item);
if(item_group)
{
//Recurse:
get_table_fields_to_show_for_sequence_add_group(table_name, table_privs, all_db_fields, *item_group, vecFields);
}
}
}
if(vecFields.empty())
{
//g_warning("Box_Data::get_table_fields_to_show_for_sequence_add_group(): Returning empty list.");
}
}
Base_DB::type_vecLayoutFields Base_DB::get_table_fields_to_show_for_sequence(const Glib::ustring& table_name, const Document_Glom::type_mapLayoutGroupSequence& mapGroupSequence) const
{
//Get field definitions from the database, with corrections from the document:
type_vecFields all_fields = get_fields_for_table(table_name);
const Privileges table_privs = get_current_privs(table_name);
//Get fields that the document says we should show:
type_vecLayoutFields result;
const Document_Glom* pDoc = dynamic_cast<const Document_Glom*>(get_document());
if(pDoc)
{
if(mapGroupSequence.empty())
{
//No field sequence has been saved in the document, so we use all fields by default, so we start with something visible:
//Start with the Primary Key as the first field:
guint iPrimaryKey = 0;
bool bPrimaryKeyFound = get_field_primary_key_index_for_fields(all_fields, iPrimaryKey);
Glib::ustring primary_key_field_name;
if(bPrimaryKeyFound)
{
sharedptr<LayoutItem_Field> layout_item(new LayoutItem_Field);
layout_item->m_field = all_fields[iPrimaryKey];
//Don't use thousands separators with ID numbers:
layout_item->m_formatting.m_numeric_format.m_use_thousands_separator = false;
layout_item->set_editable(true); //A sensible default.
//Prevent editing of the field if the user may not edit this table:
layout_item->m_priv_view = table_privs.m_view;
layout_item->m_priv_edit = table_privs.m_edit;
result.push_back(layout_item);
}
//Add the rest:
for(type_vecFields::const_iterator iter = all_fields.begin(); iter != all_fields.end(); ++iter)
{
const Field& field_info = *iter;
if(iter->get_name() != primary_key_field_name) //We already added the primary key.
{
sharedptr<LayoutItem_Field> layout_item(new LayoutItem_Field);
layout_item->m_field = field_info;
layout_item->set_editable(true); //A sensible default.
//Prevent editing of the field if the user may not edit this table:
layout_item->m_priv_view = table_privs.m_view;
layout_item->m_priv_edit = table_privs.m_edit;
result.push_back(layout_item);
}
}
}
else
{
type_vecFields vecFieldsInDocument = pDoc->get_table_fields(table_name);
//We will show the fields that the document says we should:
for(Document_Glom::type_mapLayoutGroupSequence::const_iterator iter = mapGroupSequence.begin(); iter != mapGroupSequence.end(); ++iter)
{
const LayoutGroup& group = iter->second;
if(true) //!group.m_hidden)
{
//Get the fields:
get_table_fields_to_show_for_sequence_add_group(table_name, table_privs, all_fields, group, result);
}
}
}
}
if(result.empty())
{
//g_warning("Box_Data::get_table_fields_to_show_for_sequence_add_group(): Returning empty list.");
}
return result;
}
......@@ -97,6 +97,9 @@ protected:
bool get_table_exists_in_database(const Glib::ustring& table_name) const;
type_vecFields get_fields_for_table(const Glib::ustring& table_name) const;
bool get_fields_for_table_one_field(const Glib::ustring& table_name, const Glib::ustring& field_name, Field& field) const;
bool get_field_primary_key_for_table(const Glib::ustring& table_name, Field& field) const;
Glib::ustring get_find_where_clause_quick(const Glib::ustring& table_name, const Gnome::Gda::Value& quick_search) const;
......@@ -132,6 +135,12 @@ protected:
virtual void on_userlevel_changed(AppState::userlevels userlevel);
type_vecLayoutFields get_table_fields_to_show_for_sequence(const Glib::ustring& table_name, const Document_Glom::type_mapLayoutGroupSequence& mapGroupSequence) const;
void get_table_fields_to_show_for_sequence_add_group(const Glib::ustring& table_name, const Privileges& table_privs, const type_vecFields& all_db_fields, const LayoutGroup& group, type_vecLayoutFields& vecFields) const;
static bool get_field_primary_key_index_for_fields(const type_vecFields& fields, guint& field_column);
static bool get_field_primary_key_index_for_fields(const type_vecLayoutFields& fields, guint& field_column);
static Glib::ustring util_string_from_decimal(guint decimal);
static guint util_decimal_from_string(const Glib::ustring& str);
......
......@@ -71,26 +71,6 @@ void Box_DB_Table::set_entered_field_data(const LayoutItem_Field& /* field */, c
//Override this.
}
bool Box_DB_Table::get_field_primary_key_for_table(const Glib::ustring& table_name, Field& field) const
{
const Document_Glom* document = get_document();
if(document)
{
//TODO_Performance:
Document_Glom::type_vecFields fields = document->get_table_fields(table_name);
for(Document_Glom::type_vecFields::iterator iter = fields.begin(); iter != fields.end(); ++iter)
{
if(iter->get_primary_key())
{
field = *iter;
return true;
}
}
}
return false;
}
unsigned long Box_DB_Table::get_last_auto_increment_value(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model, const Glib::ustring& /* field_name */)
{
sharedptr<SharedConnection> sharedconnection = connect_to_server();
......@@ -111,24 +91,6 @@ unsigned long Box_DB_Table::get_last_auto_increment_value(const Glib::RefPtr<Gno
return 0;
}
bool Box_DB_Table::get_fields_for_table_one_field(const Glib::ustring& table_name, const Glib::ustring& field_name, Field& field) const
{
//Initialize output parameter:
Field result = Field();
type_vecFields fields = get_fields_for_table(table_name);
type_vecFields::iterator iter = std::find_if(fields.begin(), fields.end(), predicate_FieldHasName<Field>(field_name));
if(iter != fields.end()) //TODO: Handle error?
{
field = *iter;
return true;
}
return false;
}
//static:
Box_DB_Table::type_vecFields Box_DB_Table::get_fields_for_datamodel(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model)
{
......
......@@ -42,9 +42,6 @@ public:
//TODO: Put this somewhere more sensible:
typedef std::map<Gnome::Gda::ValueType, Glib::ustring> type_map_valuetypes;
bool get_fields_for_table_one_field(const Glib::ustring& table_name, const Glib::ustring& field_name, Field& field) const;
protected:
//virtual Glib::RefPtr<Gnome::Gda::DataModel> record_new(Gnome::Gda::Value primary_key_value);
......@@ -52,7 +49,6 @@ protected:
virtual Gnome::Gda::Value get_entered_field_data(const LayoutItem_Field& field) const;
virtual void set_entered_field_data(const LayoutItem_Field& field, const Gnome::Gda::Value& value);
bool get_field_primary_key_for_table(const Glib::ustring& table_name, Field& field) const;
//static bool get_field_primary_key(const type_vecFields& fields, Field& field);
unsigned long get_last_auto_increment_value(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model, const Glib::ustring& field_name);
......
......@@ -147,7 +147,6 @@ Glib::ustring GlomConversions::get_text_for_gda_value(Field::glom_field_type glo
return get_text_for_gda_value(glom_type, value, std::locale("") /* the user's current locale */, numeric_format); //Get the current locale.
}
Glib::ustring GlomConversions::get_text_for_gda_value(Field::glom_field_type glom_type, const Gnome::Gda::Value& value, const std::locale& locale, const NumericFormat& numeric_format, bool iso_format)
{
if(value.get_value_type() == Gnome::Gda::VALUE_TYPE_NULL) //The type can be null for any of the actual field types.
......
......@@ -88,7 +88,6 @@ protected:
//Glib::ustring m_relationship_name; //bool m_related;
bool m_hidden;
bool m_formatting_use_default;
};
#endif //GLOM_DATASTRUCTURE_LAYOUTITEM_FIELD_H
......
/* Glom
*
* Copyright (C) 2001-2004 Murray Cumming
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "filechooser_export.h"
#include <gtkmm/stock.h>
#include <glibmm/i18n.h>
FileChooser_Export::FileChooser_Export()
: Gtk::FileChooserDialog(_("Export To File."), Gtk::FILE_CHOOSER_ACTION_SAVE),
m_extra_widget(false, 6),
m_button_format(_("Define Data _Format"), true /* use mnenomic */)
{
add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
add_button(_("Export"), Gtk::RESPONSE_OK);
m_extra_widget.pack_start(m_button_format, Gtk::PACK_SHRINK);
m_button_format.show();
set_extra_widget(m_extra_widget);
m_extra_widget.show();
}
FileChooser_Export::~FileChooser_Export()
{
}
/* Glom
*
* Copyright (C) 2001-2004 Murray Cumming
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef FILECHOOSER_EXPORT_GLOM_H
#define FILECHOOSER_EXPORT_GLOM_H
#include <gtkmm/filechooserdialog.h>
class FileChooser_Export :
public Gtk::FileChooserDialog
{
public:
FileChooser_Export();
virtual ~FileChooser_Export();
protected:
//Member widgets:
Gtk::HBox m_extra_widget;
Gtk::Button m_button_format;
};
#endif
......@@ -29,7 +29,9 @@
#include "data_structure/layout/report_parts/layoutitem_summary.h"
#include "data_structure/layout/report_parts/layoutitem_fieldsummary.h"
#include "relationships_overview/dialog_relationships_overview.h"
#include "filechooser_export.h"
#include <sstream> //For stringstream.
#include <fstream>
#include <glibmm/i18n.h>
Frame_Glom::Frame_Glom(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade)
......@@ -368,7 +370,7 @@ void Frame_Glom::alert_no_table()
{
//TODO: Obviously this document should have been deleted when the database-creation was cancelled.
/* Note that "canceled" is the correct US spelling. */
show_ok_dialog(_("No database"), _("This document does not specify any database. Maybe the document creation was canceled before the database could be created."), *pWindowApp);
show_ok_dialog(_("No database"), _("This document does not specify any database. Maybe the document creation was canceled before the database could be created."), *pWindowApp, Gtk::MESSAGE_ERROR);
}
}
......@@ -486,6 +488,93 @@ void Frame_Glom::on_menu_userlevel_Operator(const Glib::RefPtr<Gtk::RadioAction>
}
}
void Frame_Glom::on_menu_file_export()
{
//Start with a sequence based on the Details view:
//The user can changed this by clicking the button in the FileChooser:
const Document_Glom::type_mapLayoutGroupSequence mapGroupSequence = get_document()->get_data_layout_groups_plus_new_fields("details", m_strTableName);
type_vecLayoutFields fieldsSequence = get_table_fields_to_show_for_sequence(m_strTableName, mapGroupSequence);
if(fieldsSequence.empty())
return;
Gtk::Window* pWindowApp = get_app_window();
g_assert(pWindowApp);
//Do not try to export the data if the user may not view it:
Privileges table_privs = get_current_privs(m_strTableName);
if(!table_privs.m_view)
{
show_ok_dialog(_("Export Not Allowed."), _("You do not have permission to view the data in this table, so you may not export the data."), *pWindowApp, Gtk::MESSAGE_ERROR);
return;
}
//Ask the user for the new file location, and to optionally modify the format:
FileChooser_Export dialog;
const int response = dialog.run();
dialog.hide();
if(response == Gtk::RESPONSE_CANCEL)
return;
const std::string filepath = dialog.get_filename();
if(filepath.empty())
return;
//Add extra possibly-non-visible columns that we need:
Field field_primary_key;
const bool found = get_field_primary_key_for_table(m_strTableName, field_primary_key);
if(found)
{
sharedptr<LayoutItem_Field> layout_item(new LayoutItem_Field);
layout_item->m_field = field_primary_key;
fieldsSequence.push_back(layout_item);
}
//const int index_primary_key = fieldsSequence.size() - 1;
const Glib::ustring found_set_where_clause = m_Notebook_Data.get_where_clause();
const Glib::ustring query = GlomUtils::build_sql_select_with_where_clause(m_strTableName, fieldsSequence, found_set_where_clause);
Glib::RefPtr<Gnome::Gda::DataModel> result = Query_execute(query);
guint rows_count = 0;
if(result)
rows_count = result->get_n_rows();
if(rows_count)
{
const guint columns_count = result->get_n_columns();
std::fstream the_stream(filepath.c_str(), std::ios_base::out | std::ios_base::trunc);
if(!the_stream)
{
show_ok_dialog(_("Could Not Create File."), _("Glom could not create the specified file."), *pWindowApp, Gtk::MESSAGE_ERROR);
return;
}
for(guint row_index = 0; row_index < rows_count; ++row_index)
{
Glib::ustring row_string;
for(guint col_index = 0; col_index < columns_count; ++col_index)
{
const Gnome::Gda::Value value = result->get_value_at(row_index, col_index);
sharedptr<LayoutItem_Field> layout_item = fieldsSequence[col_index];