A Gnome::Db::Form
is in many ways similar to a Gnome::Db::Grid
except that it does not
show the data in a table but instead shows only one data set (usually one record) at a time using appropriate widgets. It features buttons to
navigate through the data set and can also be used to edit data if modification queries were given, as stated
in the Gnome::Db::Grid
section.
This examples creates a read-write Form with a query from an example sqlite database.
File: examplewindow.h
#include <gtkmm.h> #include <libgdamm.h> #include <libgnomedbmm.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(const Glib::RefPtr<Gnome::Gda::Dict>& dict); private: #ifdef GLIBMM_EXCEPTIONS_ENABLED void create_model(const Glib::RefPtr<Gnome::Gda::Dict>& dict); #else void create_model(const Glib::RefPtr<Gnome::Gda::Dict>& dict, std::auto_ptr<Glib::Error>& error); #endif Glib::RefPtr<Gnome::Gda::DataModelQuery> m_model; Gtk::VBox m_box; Gtk::Label m_label; Gnome::Db::Form* m_form; };
File: main.cc
#include <libgnomedbmm.h> #include <libgdamm.h> #include <gtkmm.h> #include <iostream> #include "examplewindow.h" #ifdef GLIBMM_EXCEPTIONS_ENABLED Glib::RefPtr<Gnome::Gda::Dict> create_dict() #else Glib::RefPtr<Gnome::Gda::Dict> create_dict(std::auto_ptr<Glib::Error>& error) #endif { Glib::RefPtr<Gnome::Gda::Dict> dict = Gnome::Gda::Dict::create(); Glib::RefPtr<Gnome::Gda::Client> client = Gnome::Gda::Client::create(); Glib::ustring connection_string = "DB_DIR=" LIBGNOMEDB_DATADIR ";DB_NAME=demo_db"; #ifdef GLIBMM_EXCEPTIONS_ENABLED // Create a connection to the embedded sqlite database Glib::RefPtr<Gnome::Gda::Connection> cnc = client->open_connection_from_string("SQLite", connection_string, "" /* username */, "" /* password */); // We use a Gda::Dict here to read the information how to map DMBS types to Gda types from an xml file. // A Gda::Dict is a powerful repository to store informations about the underlying database and read/write them // to xml in an efficient way. Besides types it can also store other informations such as functions and aggregates. dict->set_connection(cnc); dict->update_dbms_meta_data(); #else Glib::RefPtr<Gnome::Gda::Connection> cnc = client->open_connection_from_string("SQLite", connection_string, "" /* username */, "" /* password */, Gnome::Gda::ConnectionOptions(0), error); if(error.get() == NULL) { dict->set_connection(cnc); dict->update_dbms_meta_data(error); } #endif return dict; } int main(int argc, char* argv[]) { Gtk::Main kit(argc, argv); Gnome::Db::init("Form example", "1.0", argc, argv); Glib::RefPtr<Gnome::Gda::Dict> dict; #ifdef GLIBMM_EXCEPTIONS_ENABLED try { dict = create_dict(); } catch(const Glib::Error& err) { std::cerr << err.what() << std::endl; exit(1); } #else std::auto_ptr<Glib::Error> error; dict = create_dict(error); if(error.get() != NULL) { std::cerr << error->what() << std::endl; exit(1); } #endif ExampleWindow window(dict); kit.run(window); return 0; }
File: examplewindow.cc
#include "examplewindow.h" #include <libgdamm.h> #include <iostream> #ifdef GLIBMM_EXCEPTIONS_ENABLED void ExampleWindow::create_model(const Glib::RefPtr<Gnome::Gda::Dict>& dict) #else void ExampleWindow::create_model(const Glib::RefPtr<Gnome::Gda::Dict>& dict, std::auto_ptr<Glib::Error>& error) #endif // GLIBMM_EXCEPTIONS_ENABLED { Glib::RefPtr<Gnome::Gda::Query> query = Gnome::Gda::Query::create(dict); #ifdef GLIBMM_EXCEPTIONS_ENABLED query->set_sql_text("SELECT ref, category, name, price, wh_stored FROM products"); #else query->set_sql_text("SELECT ref, category, name, price, wh_stored FROM products", error); if(error.get() != NULL) return; #endif // GLIBMM_EXCEPTIONS_ENABLED m_model = Gnome::Gda::DataModelQuery::create(query); const Glib::ustring update_query = "UPDATE products set " "ref=##/*name:'+0' type:gchararray*/, " "category=##/*name:'+1' type:gint*/," "name=##/*name:'+2' type:gchararray*/, " "wh_stored=##/*name:'+4' type:gint*/ " "WHERE ref=##/*name:'-0' type:gchararray*/"; const Glib::ustring delete_query = "DELETE FROM products WHERE ref=##/*name:'-0' type:gchararray*/"; const Glib::ustring insert_query = "INSERT INTO products (ref, category, name, price, wh_stored) " "VALUES (##/*name:'+0' type:gchararray*/, " "##/*name:'+1' type:gint*/, " "##/*name:'+2' type:gchararray*/, " "1.0, " "##/*name:'+4' type:gint*/)"; #ifdef GLIBMM_EXCEPTIONS_ENABLED m_model->set_modification_query(update_query); m_model->set_modification_query(delete_query); m_model->set_modification_query(insert_query); #else m_model->set_modification_query(update_query, error); m_model->set_modification_query(delete_query, error); m_model->set_modification_query(insert_query, error); #endif } ExampleWindow::ExampleWindow(const Glib::RefPtr<Gnome::Gda::Dict>& dict) : m_label("The following Gnome::Db::Form widget displays data from the 'products' table.\n\n" "Because modification queries were provided, the data is writeable\n(except for the 'price' " "field, because these queries voluntarily omit that field)."), m_form(0) { m_box.set_border_width(6); m_box.pack_start(m_label, Gtk::PACK_SHRINK); #ifdef GLIBMM_EXCEPTIONS_ENABLED try { create_model(dict); } catch(const Glib::Error& err) { std::cerr << "Exception caught: " << err.what() << std::endl; exit(1); } #else std::auto_ptr<Glib::Error> error; create_model(dict, error); if(error.get() != NULL) { std::cerr << "Exception caught: " << error->what() << std::endl; exit(1); } #endif /* Create the demo widget */ m_form = Gtk::manage(new Gnome::Db::Form(m_model)); m_box.pack_start(*m_form); add(m_box); show_all(); }
Often you might have fields in your database that should never be empty.
Gnome::Db::Form
can support you by adding a '*' next to every field
that is mandatory and showing an error message if the
user leaves this field empty. Empty in this case means an empty string or an empty
Gnome::Gda::Value
.
To set a field as mandatory you must access the underlying Gda::Parameter
of your Gda::DataModel
and set the parameter to not-null:
// Configure param to be mandatory: // Note that this must be done before the form is created. Glib::RefPtr<Gnome::Gda::DataModelIter> iter = m_model->create_iter(); Glib::RefPtr<Gnome::Gda::Parameter> param = iter->find_param("name"); param->set_not_null();
File: examplewindow.h
#include <gtkmm.h> #include <libgdamm.h> #include <libgdamm/datamodelquery.h> #include <libgnomedbmm.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(Glib::RefPtr<Gnome::Gda::Dict>& dict); private: #ifdef GLIBMM_EXCEPTIONS_ENABLED void create_model(const Glib::RefPtr<Gnome::Gda::Dict>& dict); #else void create_model(const Glib::RefPtr<Gnome::Gda::Dict>& dict, std::auto_ptr<Glib::Error>& error); #endif // GLIBMM_EXCEPTIONS_ENABLED Glib::RefPtr<Gnome::Gda::DataModelQuery> m_model; Gtk::VBox m_box; Gtk::Label m_label; Gnome::Db::Form* m_form; };
File: main.cc
#include <libgnomedbmm.h> #include <libgdamm.h> #include <gtkmm.h> #include <iostream> #include "examplewindow.h" #ifdef GLIBMM_EXCEPTIONS_ENABLED Glib::RefPtr<Gnome::Gda::Dict> create_dict() #else Glib::RefPtr<Gnome::Gda::Dict> create_dict(std::auto_ptr<Glib::Error>& error) #endif { Glib::RefPtr<Gnome::Gda::Dict> dict = Gnome::Gda::Dict::create(); Glib::RefPtr<Gnome::Gda::Client> client = Gnome::Gda::Client::create(); Glib::ustring connection_string = "DB_DIR=" LIBGNOMEDB_DATADIR ";DB_NAME=demo_db"; #ifdef GLIBMM_EXCEPTIONS_ENABLED // Create a connection to the embedded sqlite database Glib::RefPtr<Gnome::Gda::Connection> cnc = client->open_connection_from_string("SQLite", connection_string, "" /* username */, "" /* password */); // We use a Gda::Dict here to read the information how to map DMBS types to Gda types from an xml file. // A Gda::Dict is a powerful repository to store informations about the underlying database and read/write them // to xml in an efficient way. Besides types it can also store other informations such as functions and aggregates. dict->set_connection(cnc); dict->update_dbms_meta_data(); #else Glib::RefPtr<Gnome::Gda::Connection> cnc = client->open_connection_from_string("SQLite", connection_string, "", "", Gnome::Gda::ConnectionOptions(0), error); if(error.get() == NULL) { dict->set_connection(cnc); dict->update_dbms_meta_data(error); } #endif return dict; } int main(int argc, char* argv[]) { Gtk::Main kit(argc, argv); Gnome::Db::init("Mandatory example", "1.0", argc, argv); Glib::RefPtr<Gnome::Gda::Dict> dict; #ifdef GLIBMM_EXCEPTIONS_ENABLED try { dict = create_dict(); } catch(const Glib::Error& err) { std::cerr << err.what() << std::endl; exit(1); } #else std::auto_ptr<Glib::Error> error; dict = create_dict(error); if(error.get() != NULL) { std::cerr << error->what() << std::endl; exit(1); } #endif ExampleWindow window(dict); kit.run(window); return 0; }
File: examplewindow.cc
#include "examplewindow.h" #include <libgdamm/query.h> #include <libgdamm/datamodelquery.h> #include <libgdamm/datamodel.h> #include <iostream> #ifdef GLIBMM_EXCEPTIONS_ENABLED void ExampleWindow::create_model(const Glib::RefPtr<Gnome::Gda::Dict>& dict) #else void ExampleWindow::create_model(const Glib::RefPtr<Gnome::Gda::Dict>& dict, std::auto_ptr<Glib::Error>& error) #endif // GLIBMM_EXCEPTIONS_ENABLED { Glib::RefPtr<Gnome::Gda::Query> query = Gnome::Gda::Query::create(dict); #ifdef GLIBMM_EXCEPTIONS_ENABLED query->set_sql_text("SELECT id, name, country, city FROM warehouses"); #else query->set_sql_text("SELECT id, name, country, city FROM warehouses", error); if(error.get() != NULL) return; #endif // GLIBMM_EXCEPTIONS_ENABLED // Create the demo widget: m_model = Gnome::Gda::DataModelQuery::create(query); // TODO: Explain what these SQL queries do: const Glib::ustring update_query = "UPDATE warehouses set " "id=##/*name:'+0' type:gint*/, " "name=##/*name:'+1' type:gchararray*/," "country=##/*name:'+2' type:gchararray*/, " "city=##/*name:'+3' type:gchararray*/ " "WHERE id=##/*name:'-0' type:gint*/"; const Glib::ustring delete_query = "DELETE FROM warehouses WHERE id=##/*name:'-0' type:gint*/"; const Glib::ustring insert_query = "INSERT INTO warehouses (id, name, country, city) " "VALUES (##/*name:'+0' type:gint*/, " "##/*name:'+1' type:gchararray*/, " "##/*name:'+2' type:gchararray*/, " "##/*name:'+3' type:gchararray*/)"; #ifdef GLIBMM_EXCEPTIONS_ENABLED m_model->set_modification_query(update_query); m_model->set_modification_query(delete_query); m_model->set_modification_query(insert_query); #else m_model->set_modification_query(update_query, error); m_model->set_modification_query(delete_query, error); m_model->set_modification_query(insert_query, error); #endif // GLIBMM_EXCEPTIONS_ENABLED } ExampleWindow::ExampleWindow(Glib::RefPtr<Gnome::Gda::Dict>& dict) : m_label("The following Gnome::Db::Form show how fields can become mandatory. Try to add a\n" "new dataset using the '+' button. You should get an error message if you omit a field that\n" "has been marked mandatory with a '*'"), m_form(0) { m_box.set_border_width(6); m_box.pack_start(m_label, Gtk::PACK_SHRINK); #ifdef GLIBMM_EXCEPTIONS_ENABLED try { create_model(dict); } catch(const Glib::Error& err) { std::cerr << "Exception Caught: " << err.what() << std::endl; exit(1); } #else std::auto_ptr<Glib::Error> error; create_model(dict, error); if(error.get() != NULL) { std::cerr << "Exception Caught: " << error->what() << std::endl; exit(1); } #endif // GLIBMM_EXCEPTIONS_ENABLED // Configure param to be mandatory // Note that this has to be done before the form is created m_form = Gtk::manage(new Gnome::Db::Form(m_model)); Glib::RefPtr<Gnome::Gda::DataModelIter> iter = m_form->get_raw_form()->get_current_data(); Glib::RefPtr<Gnome::Gda::Parameter> id = iter->find_param("id"); Glib::RefPtr<Gnome::Gda::Parameter> name = iter->find_param("name"); Glib::RefPtr<Gnome::Gda::Parameter> country = iter->find_param("country"); Glib::RefPtr<Gnome::Gda::Parameter> city = iter->find_param("city"); if (name && city && country && id) { // This seems to have absolutely no effect! id->set_not_null(false); name->set_not_null(true); country->set_not_null(false); city->set_not_null(false); } else { std::cerr << "Param not found" << std::endl; exit(1); } m_box.pack_start(*m_form); add(m_box); show_all(); }
In the previous examples, any modified data was not written back to the database
until the user pressed the commit button on the bottom of the form. However, there
are more so-called write-modes. To change it, you first have to obtain the
underlaying Gnome::Db::RawForm
(via Gnome::Db::Form::get_raw_form
)
and then call set_write_mode()
on it.
There are four of these write modes:
Table 5.1. Defined write modes
Gnome::Db::DATA_WIDGET_WRITE_ON_DEMAND | Modifications are written back on explicit demand (in this case all the modifications made to any row are written back). This is the default. |
Gnome::Db::DATA_WIDGET_WRITE_ON_ROW_CHANGE | Modifications are written back when the current row changes (in this case only the modifications on the current row are written back) |
Gnome::Db::DATA_WIDGET_WRITE_ON_VALUE_ACTIVATED | Modifications are written back when the user "activates" the data entry (for example by pressing ENTER in a single line data entry), in this case only the modifications on the current row are written back |
Gnome::Db::DATA_WIDGET_WRITE_ON_VALUE_CHANGE | Modifications are written back everytime a value changes; in this case only the modifications on the current row are written back (this option should only be used if the "source" data model accepts modifications without any test and without processing delays). |
The complete call to change the write mode to, say,
WRITE_ON_VALUE_ACTIVATED, looks like this (suppose form is a
Gnome::Db::Form
):
form.get_raw_form()->set_write_mode(Gnome::Db::DATA_WIDGET_WRITE_ON_VALUE_ACTIVATED);
Note that the set_write_mode()
function is provided by
the Gnome::Db::DataWidget
base class from which also
Gnome::Db::RawGrid
derives. This means that it is
possible to change the write mode of a grid in the same way. The only difference
is that the WRITE_ON_VALUE_CHANGE mode is equivalent to the
WRITE_ON_VALUE_ACTIVATED mode for the grid.