Table of Contents
Sometimes it makes sense to restrict the possible field values that the user can enter into a form or grid. Suppose you have a table that contains orders for some customers. For that customers field, it might makes sense to only ever contain the name of an existing customer rather than any possible freeform text.
To achieve this, we should firstly create a Gnome::Gda::DataModel
holding the possible values for the customers field, by creating a Gnome::Gda::DataModelArray
.
const Glib::ustring customers[] = { "Ed Lamton", "Lew Bonito", "Mark Lawrenscep", "Grep Popoff", "Vladimir Zirkov" }; Glib::RefPtr<Gnome::Gda::DataModelArray> restrict_model = Gnome::Gda::DataModelArray::create(1 /* number of columns */); for(int i = 0; i < 5 /* number of rows */; ++ i) { // Vector containing the values for each column in this row. Since we only // have one column, the vector contains only one element. std::vector<Gnome::Gda::Value> row_values; row_values.push_back(Gnome::Gda::Value(customers[i])); restrict_model->append_values(row_values); }
The first line defines the customer names to which we would like to restrict the values. Then
a new Gnome::Gda::DataModelArray
is created with one single column.
This column will contain these customer names. It is also possible to create a
model with more columns, to assign additional information to each value. Next, each
string is converted into a Gnome::Gda::Value
.
Gnome::Gda::Value
is a generic value object that can hold
a value of any type that is commonly used in database management systems. The
value is then packed into a vector that is given to the previously-created model.
The number of elements in the vector must match the number of columns of the model -
this is also why we create a vector with only one single element in it.
To actually perform the restriction on a grid or form, the following steps are
required, assuming that grid
is a Gnome::Db::Grid
):
Glib::RefPtr<Gnome::Gda::DataModelIter> iter = grid.get_raw_grid()->get_current_data(); Glib::RefPtr<Gnome::Gda::Parameter> param = iter->find_param("customer"); param->restrict_values(restrict_model, 0);
Every field in the table is represented by a Gnome::Gda::Parameter
object
in libgda. It might be looked up by its field name from a Gnome::Gda::DataModelIter
.
which we in turn get from the grid (or form respectively). Such an iterator may be used
to traverse row-wise through a Gnome::Gda::DataModel
and it also
manages the parameters for each column. The last line finally restricts the
values for this parameter to those contained in the first column (index 0) of
the model created above.
While this basically works, a much more elegant way to achieve the same functionality is to read the restriction values from another table in the database. Suppose there is another table called "customers" that, in addition to a unique ID, stores the available customer names. The "orders" table then just holds an integer ID referring to the corresponding entry in the customers table instead of the customer name itself.
Instead of the Gnome::Gda::DataModelArray
, another
Gnome::Gda::DataModelQuery
is now required that reads
the customer names from the corresponding table. The assignment of that model
to the Gnome::Gda::Parameter
works as above.
Glib::RefPtr<Gnome::Gda::DataModelQuery> model = Gnome::Gda::DataModelQuery::create(dict); model->set_sql_text("SELECT id, name FROM customers"); Glib::RefPtr<Gnome::Gda::DataModelIter> iter = grid.get_raw_grid()->get_current_data(); Glib::RefPtr<Gnome::Gda::Parameter> param = iter->find_param("customer"); param->restrict_values(model, 0);
Note that libgnomedb is smart enough to not show
the numerical ID which is contained in the 'customer' field of the 'orders' table.
Instead, it takes the 'name' of the customer that is stored in the second
column of model
(because the name was mentioned in the SELECT
query when using set_sql_text()
in our example). If our SELECT
command had requested only the 'id' field,
then libgnomedb would have shown only the ID field.
model
.
File: examplewindow.h
#include <memory> #include <gtkmm.h> #include <libgdamm.h> #include <libgnomedbmm.h> class ExampleWindow : public Gtk::Window { public: ExampleWindow(const Glib::RefPtr<Gnome::Gda::Dict>& dict); private: Glib::RefPtr<Gnome::Gda::DataModelQuery> m_model; Gtk::VBox m_box; Gtk::Label m_label; std::auto_ptr<Gnome::Db::Grid> m_grid; };
File: examplewindow.cc
#include "examplewindow.h" #include <libgdamm.h> #include <iostream> ExampleWindow::ExampleWindow(const Glib::RefPtr<Gnome::Gda::Dict>& dict) { m_box.set_border_width(6); m_box.pack_start(m_label, Gtk::PACK_SHRINK); // Select relevant order fields from database Glib::RefPtr<Gnome::Gda::Query> query = Gnome::Gda::Query::create(dict); query->set_sql_text("SELECT id, customer, creation_date FROM orders"); m_model = Gnome::Gda::DataModelQuery::create(query); // This selects fields from the customers table to restrict the customer // field in the datamodel above to the values that are in the customers // table. We also select name to show the user the name of the customer // in the grid instead of just its ID (as it would appear in the orders // table). Glib::RefPtr<Gnome::Gda::Query> restr_query = Gnome::Gda::Query::create(dict); restr_query->set_sql_text("SELECT id, name FROM customers"); Glib::RefPtr<Gnome::Gda::DataModel> restr = Gnome::Gda::DataModelQuery::create(restr_query); try { m_model->set_modification_query( "UPDATE orders set " "customer=##/*name:'+1' type:gint*/ " "WHERE id=##/*name:'-0' type:gint*/"); m_model->set_modification_query( "DELETE FROM orders WHERE id=##/*name:'-0' type:gint*/"); m_model->set_modification_query( "INSERT INTO orders (customer) " "VALUES (##/*name:'+1' type:gint*/)"); } catch(const Glib::Error& err) { std::cerr << "Exception caught: " << err.what() << std::endl; exit(1); } // Lookup the Gda::Parameter object that corresponds to the customer field // in the order table. We find it by its field name "customer". m_grid.reset(new Gnome::Db::Grid(m_model)); Glib::RefPtr<Gnome::Gda::DataModelIter> iter = m_grid->get_raw_grid()->get_current_data(); Glib::RefPtr<Gnome::Gda::Parameter> param = iter->find_param("customer"); // Restrict values to those in the first column of the customers table. // Here is also some more gda magic involved so that the grid actually // shows the name of the customer instead of the ID. I am not absolutely // sure how this works. param->restrict_values(restr, 0); m_box.pack_start(*m_grid); add(m_box); set_default_size(400, 400); show_all(); }
File: main.cc
#include <libgnomedbmm.h> #include <libgdamm.h> #include <gtkmm.h> #include <iostream> #include "examplewindow.h" int main(int argc, char* argv[]) { Gtk::Main kit(argc, argv); Gnome::Db::init("Grid example", "1.0", argc, argv); Glib::RefPtr<Gnome::Gda::Dict> dict = Gnome::Gda::Dict::create(); try { std::string filename = Glib::build_filename(LIBGNOMEDB_DATADIR, "demo_dict.xml"); dict->load_xml_file(filename); Glib::RefPtr<Gnome::Gda::Client> client = Gnome::Gda::Client::create(); Glib::ustring connection_string = "DB_DIR=" LIBGNOMEDB_DATADIR ";DB_NAME=demo_db"; Glib::RefPtr<Gnome::Gda::Connection> cnc = client->open_connection_from_string("SQLite", connection_string, "", "", Gnome::Gda::ConnectionOptions(0)); dict->set_connection(cnc); } catch(const Glib::Error& err) { std::cerr << "Exception caught: " << err.what() << std::endl; return 1; } ExampleWindow window(dict); kit.run(window); return 0; }