Chapter 5. Widgets

Table of Contents

This sections explains the additional widgets provided by libgnomedbmm.

Grid

A Gnome::Db::Grid is used to display and edit data in a table. In the simple read-only case it is filled with the result of a Gda::Query on the underlying database. A Gda::Model must be filled with that query.

You do not need to show all fields of a table within the grid but only those will be shown that you select in your query:

/* Show all fields: */
query->set_sql_text("SELECT ref, category, name, price, wh_stored FROM products");
/* Show only some fields: */
query->set_sql_text("SELECT name, price FROM products");

The more interesting read-write case obviously requires some more work. The Gnome::Db::Grid must know how to modify the database if the user changes some fields. This information must be given to the model by using the set_modification_query() method. The details of the modification query depend on your database engine and layout.

Note that the editing capabilities of the grid depend on the modification queries provided. If no delete query is given, rows cannot be deleted. If the update query does not update some field ('price' in the example), values in the corresponding column cannot be modified.

Gnome::Db::Grid reference

Example

This examples creates a read-write Grid with a query from an example sqlite database.

Figure 5.1. Grid

Grid

Source Code

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:
#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::Grid* m_grid;
};

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
  Glib::RefPtr<Gnome::Gda::Connection> cnc = client->open_connection_from_string("SQLite", connection_string, "" /* username */, "" /* password */);
  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("Grid 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 << "Exception caught: " << err.what() << std::endl;
    return 1;
  }
#else
  std::auto_ptr<Glib::Error> error;
  dict = create_dict(error);
  if(error.get() != NULL)
  {
    std::cerr << "Exception caught: " << error->what() << std::endl;
    return 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::Grid widget displays data from the 'products' table.\n\n"
          "As modification queries are provided, the data is read-write\n(except for the 'price' "
          "field as these queries voluntarily omit that field).")
{    
  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_grid = Gtk::manage(new Gnome::Db::Grid(m_model));
  m_box.pack_start(*m_grid);
  add(m_box);
  set_default_size(0, 400);
  show_all();
}

Changing column titles

By default, the title headers of the columns on a grid contain the name of the field. This might not always be desirable because they might not be human-readable. An easy way to specify custom headers is to adjust the SELECT query which reads the actual data:

query->set_sql_text("SELECT ref as \"ID\", category as \"Category\" FROM products");

The 'as' clause specifies the new name.

However, since Gnome::Db::Grid is implemented as a regular Gtk::TreeView, the TreeView API can be used to alter the title of the individual columns. Likewise, it is also possible in this way to completely hide a column or manipulate it in other ways.

Gnome::Db::Grid contains a Gnome::Db::RawGrid, which is a derived TreeView which shows the data. The Gnome::Db::Grid also provides scrolling functionality and the toolbar at the bottom. This means that, in order to use the TreeView API, we need the Gnome::Db::RawGrid object from the grid. This is available via the Gnome::Db::Grid::get_raw_grid() function.

The Gtk::TreeView::get_column() function provides access to the Gtk::TreeViewColumn for a given column. The index to get_column() depends on your selection query. For instance, if it was "SELECT ref, category FROM products", then ref will be the first column (with index 0) and category will be the second (with index 1).

For instance, our code might look like this, assuming model is a valid Gnome::Gda::DataModel.

Gnome::Db::Grid grid(model);
grid.get_raw_grid()->get_column(0)->set_title("ID");
grid.get_raw_grid()->get_column(1)->set_title("Category");

The first column's title was changed to "ID" whereas the second was changed to "Category".

Example

Source Code

File: main.cc

#include <libgnomedbmm.h>
#include <gtkmm.h>

int main(int argc, char* argv[])
{
  Gtk::Main kit(argc, argv);
  Gnome::Db::init("Example", "1.0", argc, argv);

#ifndef GLIBMM_EXCEPTIONS_ENABLED
  std::auto_ptr<Glib::Error> error;
#endif

  Glib::RefPtr<Gnome::Gda::Client> client = Gnome::Gda::Client::create();
#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<Gnome::Gda::Connection> connection = client->open_connection_from_string("SQLite", "DB_DIR=" LIBGNOMEDB_DATADIR ";DB_NAME=demo_db", "" /* username */, "" /* password */);
#else
  Glib::RefPtr<Gnome::Gda::Connection> connection = client->open_connection_from_string("SQLite", "DB_DIR=" LIBGNOMEDB_DATADIR ";DB_NAME=demo_db", "" /* username */, "" /* password */, Gnome::Gda::ConnectionOptions(0), error);
  if(error.get() != NULL) return -1;
#endif // GLIBMM_EXCEPTIONS_ENABLED

#ifdef GLIBMM_EXCEPTIONS_ENABLED
  Glib::RefPtr<Gnome::Gda::DataModel> model = connection->execute_select_command("SELECT * FROM products");
#else
  Glib::RefPtr<Gnome::Gda::DataModel> model = connection->execute_select_command("SELECT * FROM products", error);
  if(error.get() != NULL) return -1;
#endif // GLIBMM_EXCEPTIONS_ENABLED

  Gnome::Db::Grid grid(model);
  Gnome::Db::RawGrid* raw_grid = grid.get_raw_grid();
  raw_grid->get_column(0)->set_title("ID");
  raw_grid->get_column(1)->set_title("Category");
  raw_grid->get_column(2)->set_title("Name");
  raw_grid->get_column(3)->set_title("Price");
  raw_grid->get_column(4)->set_title("Stored");

  Gtk::Window window;
  window.set_title("Libgnomedbmm example window");
  window.set_default_size(400, 400);
  window.add(grid);
  window.show_all();

  kit.run(window);
  return 0;
}