[glom/choices_celllayout] Choices combos: Reuse the list view implementation. Only works for Combo now.



commit 5b00e24eda2d69acb2afc924bca9a994ac71933b
Author: Murray Cumming <murrayc murrayc com>
Date:   Thu Sep 30 12:55:36 2010 +0200

    Choices combos: Reuse the list view implementation. Only works for Combo now.
    
    	* glom/mode_data/db_adddel/treemodel_db.[h|cc]: Moved to:
    	* glom/mode_data/datawidget/treemodel_db.[h|cc]: so we can use it for the
    	choices combo widgets too.
    	* glom/mode_data/datawidget/combochoices.[h|cc]:
    	set_choices_related(): Make this pure virtual.
    	Remove set_choices_with_second().
    	* glom/mode_data/datawidget/cellcreation.[h|cc]: create_cell():
    	Use set_choices_related() instead of getting all rows as a list and
    	using set_choices_with_second().
    	* glom/datawidget/combochoiceswithtreemodel.[h|cc]:
    	Override set_choices_related() and cell_connect_cell_data_func().
    	Remove the use_model() pure virtual method.
    	* glom/mode_data/datawidget/cellrenderer_dblist.[h|cc]:
    	* glom/mode_data/datawidget/combo.[h|cc]:
    	* glom/mode_data/datawidget/comboentry.[h|cc]:
    	* glom/mode_data/datawidget/combo_as_radio_buttons.[h|cc]: Adapt,
    	setting up the view in overrides of set_choices_fixed() and
    	set_choices_related() instead of use_model(), and being value-based
    	where possible instead of converting values to and from text.
    
    	* glom/mode_data/db_adddel/db_adddel.[h|cc]: Move create_model_db() to
    	DbTreeModel::create_from_items() so we can use it for the combo choice
    	widgets too.
    	refresh_cell_choices_data_from_database_with_foreign_key():
    	Use set_choices_related() instead of getting all rows as a list and
    	using set_choices_with_second().

 ChangeLog                                          |   31 +++
 Makefile_glom.am                                   |    4 +-
 glom/mode_data/box_data.cc                         |    2 +-
 glom/mode_data/box_data_list.cc                    |    1 -
 glom/mode_data/datawidget/cellcreation.cc          |    6 +-
 glom/mode_data/datawidget/cellcreation.h           |    2 +-
 glom/mode_data/datawidget/cellrenderer_dblist.cc   |   60 ++++--
 glom/mode_data/datawidget/cellrenderer_dblist.h    |   11 +-
 glom/mode_data/datawidget/combo.cc                 |  189 ++++++++++---------
 glom/mode_data/datawidget/combo.h                  |   14 +-
 .../mode_data/datawidget/combo_as_radio_buttons.cc |    9 +-
 glom/mode_data/datawidget/combo_as_radio_buttons.h |   14 +-
 glom/mode_data/datawidget/combochoices.cc          |    8 +-
 glom/mode_data/datawidget/combochoices.h           |    9 +-
 .../datawidget/combochoiceswithtreemodel.cc        |  202 +++++++++++++++++++-
 .../datawidget/combochoiceswithtreemodel.h         |   35 +++-
 glom/mode_data/datawidget/comboentry.cc            |   68 ++++++-
 glom/mode_data/datawidget/comboentry.h             |    8 +-
 glom/mode_data/datawidget/datawidget.cc            |    2 +-
 .../{db_adddel => datawidget}/treemodel_db.cc      |  103 ++++++++++-
 .../{db_adddel => datawidget}/treemodel_db.h       |   23 ++-
 glom/mode_data/db_adddel/db_adddel.cc              |  129 +------------
 glom/mode_data/db_adddel/db_adddel.h               |    2 +-
 po/POTFILES.in                                     |    1 +
 24 files changed, 640 insertions(+), 293 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index fb63762..b8cff05 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2010-10-02  Murray Cumming  <murrayc murrayc com>
+
+	Choices combos: Reuse the list view implementation. Only works for Combo now.
+
+	* glom/mode_data/db_adddel/treemodel_db.[h|cc]: Moved to:
+	* glom/mode_data/datawidget/treemodel_db.[h|cc]: so we can use it for the 
+	choices combo widgets too.
+	* glom/mode_data/datawidget/combochoices.[h|cc]:
+	set_choices_related(): Make this pure virtual.
+	Remove set_choices_with_second().
+	* glom/mode_data/datawidget/cellcreation.[h|cc]: create_cell():
+	Use set_choices_related() instead of getting all rows as a list and 
+	using set_choices_with_second().
+	* glom/datawidget/combochoiceswithtreemodel.[h|cc]:
+	Override set_choices_related() and cell_connect_cell_data_func().
+	Remove the use_model() pure virtual method.
+	* glom/mode_data/datawidget/cellrenderer_dblist.[h|cc]:
+	* glom/mode_data/datawidget/combo.[h|cc]:
+	* glom/mode_data/datawidget/comboentry.[h|cc]:
+	* glom/mode_data/datawidget/combo_as_radio_buttons.[h|cc]: Adapt, 
+	setting up the view in overrides of set_choices_fixed() and 
+	set_choices_related() instead of use_model(), and being value-based 
+	where possible instead of converting values to and from text.
+
+	* glom/mode_data/db_adddel/db_adddel.[h|cc]: Move create_model_db() to 
+	DbTreeModel::create_from_items() so we can use it for the combo choice 
+	widgets too.
+	refresh_cell_choices_data_from_database_with_foreign_key(): 
+	Use set_choices_related() instead of getting all rows as a list and 
+	using set_choices_with_second().
+	
 2010-10-01  Murray Cumming  <murrayc murrayc com>
 
 	Fix (unlikely) possible null dereferences shown by cppcheck.
diff --git a/Makefile_glom.am b/Makefile_glom.am
index 197d3cd..cc019a9 100644
--- a/Makefile_glom.am
+++ b/Makefile_glom.am
@@ -101,8 +101,6 @@ glom_source_files = \
 	glom/mode_data/db_adddel/db_adddel_withbuttons.h		\
 	glom/mode_data/db_adddel/db_treeviewcolumn_glom.cc	\
 	glom/mode_data/db_adddel/db_treeviewcolumn_glom.h		\
-	glom/mode_data/db_adddel/treemodel_db.cc		\
-	glom/mode_data/db_adddel/treemodel_db.h \
 	glom/mode_data/flowtablewithfields.cc				\
 	glom/mode_data/flowtablewithfields.h				\
 	glom/mode_data/notebook_data.cc					\
@@ -141,6 +139,8 @@ glom_source_files = \
 	glom/mode_data/datawidget/combochoiceswithtreemodel.h			\
 	glom/mode_data/datawidget/combo_as_radio_buttons.cc			\
 	glom/mode_data/datawidget/combo_as_radio_buttons.h			\
+	glom/mode_data/datawidget/treemodel_db.cc		\
+	glom/mode_data/datawidget/treemodel_db.h \
 	glom/mode_find/box_data_details_find.cc				\
 	glom/mode_find/box_data_details_find.h				\
 	glom/mode_find/box_data_list_find.cc				\
diff --git a/glom/mode_data/box_data.cc b/glom/mode_data/box_data.cc
index e6f2cde..edcb3cd 100644
--- a/glom/mode_data/box_data.cc
+++ b/glom/mode_data/box_data.cc
@@ -269,7 +269,7 @@ Document::type_list_layout_groups Box_Data::get_data_layout_groups(const Glib::u
     {
       //Get the layout information from the document:
       layout_groups = document->get_data_layout_groups_plus_new_fields(layout_name, m_table_name, layout_platform);
-
+      document->fill_layout_field_details(m_table_name, layout_groups); //TODO: Do this automatically in Document?
       const Privileges table_privs = Privs::get_current_privs(m_table_name);
 
       //Fill in the field information for the fields mentioned in the layout:
diff --git a/glom/mode_data/box_data_list.cc b/glom/mode_data/box_data_list.cc
index 4b66b7b..d6a5e56 100644
--- a/glom/mode_data/box_data_list.cc
+++ b/glom/mode_data/box_data_list.cc
@@ -26,7 +26,6 @@
 #include <libglom/privs.h>
 #include <libglom/db_utils.h>
 #include <libglom/utils.h> //For bold_message()).
-//#include <../utility_widgets/db_adddel/treemodel_db.h> //For DbTreeModel.
 #include <sstream> //For stringstream
 #include <glibmm/i18n.h>
 
diff --git a/glom/mode_data/datawidget/cellcreation.cc b/glom/mode_data/datawidget/cellcreation.cc
index d1f0bd4..510c96d 100644
--- a/glom/mode_data/datawidget/cellcreation.cc
+++ b/glom/mode_data/datawidget/cellcreation.cc
@@ -63,7 +63,7 @@ static void apply_formatting(Gtk::CellRenderer* renderer, const sharedptr<const
     text_renderer->property_background() = bg;
 }
 
-Gtk::CellRenderer* create_cell(const sharedptr<const LayoutItem>& layout_item, const Glib::ustring& table_name, Document* document, guint fixed_cell_height)
+Gtk::CellRenderer* create_cell(const sharedptr<const LayoutItem>& layout_item, const Glib::ustring& table_name, const Document* document, guint fixed_cell_height)
 {
   Gtk::CellRenderer* cell = 0;
 
@@ -213,9 +213,7 @@ Gtk::CellRenderer* create_cell(const sharedptr<const LayoutItem>& layout_item, c
         //TODO: Update this when the relationship's field value changes:
         if(choice_show_all) //Otherwise it must change whenever the relationships's ID value changes.
         {
-          const Utils::type_list_values_with_second list_values =
-            Utils::get_choice_values_all(document, item_field);
-          pCellRendererDbList->set_choices_with_second(list_values);
+          pCellRendererDbList->set_choices_related(document, item_field, Gnome::Gda::Value() /* TODO: Makes no sense */);
         }
       }
     }
diff --git a/glom/mode_data/datawidget/cellcreation.h b/glom/mode_data/datawidget/cellcreation.h
index 0ef1a47..05b38af 100644
--- a/glom/mode_data/datawidget/cellcreation.h
+++ b/glom/mode_data/datawidget/cellcreation.h
@@ -30,7 +30,7 @@ namespace Glom
 /** Create a Gtk::CellRenderer that's appropriate to display a layout item,
  * for internal use by a DbAddDel or ComboChoices widget.
  */
-Gtk::CellRenderer* create_cell(const sharedptr<const LayoutItem>& layout_item, const Glib::ustring& table_name, Document* document, guint fixed_cell_height);
+Gtk::CellRenderer* create_cell(const sharedptr<const LayoutItem>& layout_item, const Glib::ustring& table_name, const Document* document, guint fixed_cell_height);
 
 } //namespace Glom
 
diff --git a/glom/mode_data/datawidget/cellrenderer_dblist.cc b/glom/mode_data/datawidget/cellrenderer_dblist.cc
index 7e6a64a..fa64d5a 100644
--- a/glom/mode_data/datawidget/cellrenderer_dblist.cc
+++ b/glom/mode_data/datawidget/cellrenderer_dblist.cc
@@ -19,6 +19,7 @@
  */
 
 #include "cellrenderer_dblist.h"
+#include <glom/mode_data/datawidget/cellcreation.h>
 #include <gtkmm.h>
 #include <libglom/data_structure/glomconversions.h>
 
@@ -27,7 +28,8 @@ namespace Glom
 {
 
 CellRendererDbList::CellRendererDbList()
-: m_repacked_first_cell(false)
+: m_repacked_first_cell(false),
+  m_document(0)
 {
 }
 
@@ -36,8 +38,10 @@ CellRendererDbList::~CellRendererDbList()
 }
 
 
-void CellRendererDbList::use_model()
+void CellRendererDbList::set_choices_fixed(const FieldFormatting::type_list_values& list_values)
 {
+  ComboChoicesWithTreeModel::set_choices_fixed(list_values);
+
   Glib::RefPtr<Gtk::TreeModel> model = get_choices_model();
 
   //Show model in the view:
@@ -48,6 +52,23 @@ void CellRendererDbList::use_model()
   //The other cells are added in on_editing_started().
 }
 
+void CellRendererDbList::set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value)
+{
+  ComboChoicesWithTreeModel::set_choices_related(document, layout_field, foreign_key_value);
+
+  Glib::RefPtr<Gtk::TreeModel> model = get_choices_model();
+
+  //Show model in the view:
+  property_model() = model;
+  property_text_column() = 0; //TODO: This must be a text column, in m_refModel.
+  property_editable() = true; //It would be useless if we couldn't edit it.
+
+  //The other cells are added in on_editing_started(),
+  //which uses the document.
+  m_document = document;
+}
+
+
 void CellRendererDbList::set_restrict_values_to_list(bool val)
 {
   property_has_entry() = !val;
@@ -65,36 +86,46 @@ void CellRendererDbList::on_editing_started(Gtk::CellEditable* cell_editable, co
   {
     //Get the default column, created by set_text_column():
     Gtk::CellRendererText* cell = dynamic_cast<Gtk::CellRendererText*>(combobox->get_first_cell());
-        
+
     //Unpack and repack it with expand=false instead of expand=true:
     //We don't expand the first column, so we can align the other columns.
     cell->reference();
     combobox->clear();
     combobox->pack_start(*cell, false);
     cell->unreference();
-  
+
     //Make the renderer render the column:
     combobox->add_attribute(*cell, "text", 0);
-      
-    cell->property_xalign() = 0.0f;  
-    
+
+    cell->property_xalign() = 0.0f;
+
     m_repacked_first_cell = true; //Avoid doing this again.
   }
-   
-  //Add extra cells:     
+
+  //Add extra cells:
   Glib::ListHandle<Gtk::CellRenderer*> cells = combobox->get_cells();
   if(cells.size() < m_vec_model_columns.size())
   {
     for(guint col = cells.size(); col != m_vec_model_columns.size(); ++col)
     {
-      Gtk::CellRendererText* cell = Gtk::manage(new Gtk::CellRendererText);
+      Gtk::CellRenderer* cell = 0;
+      if(m_db_layout_items.empty())
+        cell = Gtk::manage(new Gtk::CellRendererText);
+      else if(col < m_db_layout_items.size())
+      {
+        sharedptr<const LayoutItem_Field> layout_item = m_db_layout_items[col];
+        cell = create_cell(layout_item, m_table_name, m_document, 0 /* fixed_cell_height */);
+      }
+
+      if(!cell)
+        continue;
 
       //Use the renderer:
       combobox->pack_start(*cell, true);
-      
+
       //Make the renderer render the column:
       combobox->add_attribute(*cell, "text", col);
-      
+
       cell->property_xalign() = 0.0f;
     }
   }
@@ -154,11 +185,6 @@ Glib::ustring CellRendererDbList::get_text() const
   return property_text();
 }
 
-void CellRendererDbList::set_choices_with_second(const type_list_values_with_second& list_values)
-{
-  DataWidgetChildren::ComboChoicesWithTreeModel::set_choices_with_second(list_values);
-}
-
 
 
 } //namespace Glom
diff --git a/glom/mode_data/datawidget/cellrenderer_dblist.h b/glom/mode_data/datawidget/cellrenderer_dblist.h
index f48c0cb..71e60a2 100644
--- a/glom/mode_data/datawidget/cellrenderer_dblist.h
+++ b/glom/mode_data/datawidget/cellrenderer_dblist.h
@@ -41,13 +41,16 @@ public:
   CellRendererDbList();
   virtual ~CellRendererDbList();
 
-  void set_choices_with_second(const type_list_values_with_second& list_values);
+  //This creates a simple ListStore, with a text cell renderer.
+  virtual void set_choices_fixed(const FieldFormatting::type_list_values& list_values);
+
+  //This creates a db-based tree model, with appropriate cell renderers:
+  virtual void set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value);
 
   void set_restrict_values_to_list(bool val = true);
 
 private:
 
-  virtual void use_model();
   virtual void on_editing_started(Gtk::CellEditable* cell_editable, const Glib::ustring& path);
 
   virtual void set_value(const Gnome::Gda::Value& value);
@@ -55,8 +58,10 @@ private:
 
   void set_text(const Glib::ustring& text);
   Glib::ustring get_text() const;
-  
+
   bool m_repacked_first_cell;
+
+  const Document* m_document;
 };
 
 } //namespace Glom
diff --git a/glom/mode_data/datawidget/combo.cc b/glom/mode_data/datawidget/combo.cc
index c19f110..1960d91 100644
--- a/glom/mode_data/datawidget/combo.cc
+++ b/glom/mode_data/datawidget/combo.cc
@@ -21,9 +21,12 @@
 #include "combo.h"
 #include <libglom/data_structure/glomconversions.h>
 #include <gtkmm/messagedialog.h>
+#include <glom/mode_data/datawidget/cellcreation.h>
 #include <glom/dialog_invalid_data.h>
 #include <libglom/data_structure/glomconversions.h>
+#include <libglom/db_utils.h>
 #include <glom/application.h>
+#include <glom/utils_ui.h>
 #include <glibmm/i18n.h>
 //#include <sstream> //For stringstream
 
@@ -69,13 +72,20 @@ ComboGlom::~ComboGlom()
 {
 }
 
-void ComboGlom::use_model()
+void ComboGlom::set_choices_fixed(const FieldFormatting::type_list_values& list_values)
 {
+  ComboChoicesWithTreeModel::set_choices_fixed(list_values);
+
   Glib::RefPtr<Gtk::TreeModel> model = get_choices_model();
+  if(!model)
+  {
+    std::cerr << G_STRFUNC << ": model is null." << std::endl;
+    return;
+  }
 
   //Show the model in the view:
   set_model(model);
-  
+
   clear();
 
   const guint columns_count = model->get_n_columns();
@@ -98,35 +108,49 @@ void ComboGlom::use_model()
   }
 }
 
-void ComboGlom::check_for_change()
+void ComboGlom::set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value)
 {
-  Glib::ustring new_text = get_text();
-  if(new_text != m_old_text)
+  ComboChoicesWithTreeModel::set_choices_related(document, layout_field, foreign_key_value);
+
+  Glib::RefPtr<Gtk::TreeModel> model = get_choices_model();
+  if(!model)
   {
-    //Validate the input:
-    bool success = false;
+    std::cerr << G_STRFUNC << ": model is null." << std::endl;
+    return;
+  }
 
-    sharedptr<const LayoutItem_Field> layout_item = sharedptr<const LayoutItem_Field>::cast_dynamic(get_layout_item());
-    const Gnome::Gda::Value value = Conversions::parse_value(layout_item->get_glom_type(), new_text, layout_item->get_formatting_used().m_numeric_format, success);
+  //Show the model in the view:
+  set_model(model);
 
-    if(success)
-    {
-      //Actually show the canonical text:
-      set_value(value);
-      m_signal_edited.emit(); //The text was edited, so tell the client code.
-    }
-    else
+  clear();
+
+  guint model_column_index = 0;
+  for(type_vec_const_layout_items::const_iterator iter = m_db_layout_items.begin(); iter != m_db_layout_items.end(); ++iter)
+  {
+    const sharedptr<const LayoutItem> layout_item = *iter;
+    if(!layout_item) //column_info.m_visible)
+      continue;
+
+    //Add the ViewColumn
+    Gtk::CellRenderer* cell = create_cell(layout_item, m_table_name, document, get_fixed_cell_height(*this));
+    if(cell)
     {
-      //Tell the user and offer to revert or try again:
-      bool revert = glom_show_dialog_invalid_data(layout_item->get_glom_type());
-      if(revert)
-      {
-        set_text(m_old_text);
-      }
-      else
-        grab_focus(); //Force the user back into the same field, so that the field can be checked again and eventually corrected or reverted.
+      //Use the renderer:
+      //We don't expand the first column, so we can align the other columns.
+      //Otherwise the other columns appear center-aligned.
+      //This bug is relevant: https://bugzilla.gnome.org/show_bug.cgi?id=629133
+      pack_start(*cell, false);
+
+      cell_connect_cell_data_func(this, cell, model_column_index);
+
+      ++model_column_index;
     }
-  }
+  } //for
+}
+
+void ComboGlom::check_for_change()
+{
+  m_signal_edited.emit();
 }
 
 void ComboGlom::set_value(const Gnome::Gda::Value& value)
@@ -134,8 +158,44 @@ void ComboGlom::set_value(const Gnome::Gda::Value& value)
   sharedptr<const LayoutItem_Field> layout_item = sharedptr<const LayoutItem_Field>::cast_dynamic(get_layout_item());
   if(!layout_item)
     return;
+ 
+  m_old_value = value;
+
+  Glib::RefPtr<Gtk::TreeModel> model = get_choices_model();
+  if(!model)
+  {
+    std::cerr << G_STRFUNC << ": model is null." << std::endl;
+    return;
+  }
+
+  bool found = false;
+  for(Gtk::TreeModel::iterator iter = model->children().begin(); iter != model->children().end(); ++iter)
+  {
+    const Gtk::TreeModel::Row row = *iter;
+    Gnome::Gda::Value this_value;
+    row.get_value(0, this_value);
+
+    if(this_value == value)
+    {
+      found = true;
+      #ifndef GLOM_ENABLE_MAEMO
+      set_active(iter);
+      #else
+      set_selected(iter);
+      #endif //GLOM_ENABLE_MAEMO
+      break;
+    }
+  }
 
-  set_text(Conversions::get_text_for_gda_value(layout_item->get_glom_type(), value, layout_item->get_formatting_used().m_numeric_format));
+  if(!found)
+  {
+    //Not found, so mark it as blank:
+    #ifndef GLOM_ENABLE_MAEMO
+    unset_active();
+    #else
+    unselect();
+    #endif
+  }
 
   //Show a different color if the value is numeric, if that's specified:
   if(layout_item->get_glom_type() == Field::TYPE_NUMERIC)
@@ -160,70 +220,25 @@ void ComboGlom::set_value(const Gnome::Gda::Value& value)
   }
 }
 
-void ComboGlom::set_text(const Glib::ustring& text)
-{
-  m_old_text = text;
-
-  Glib::RefPtr<Gtk::TreeModel> model = get_choices_model();
-  for(Gtk::TreeModel::iterator iter = model->children().begin(); iter != model->children().end(); ++iter)
-  {
-    const Gtk::TreeModel::Row row = *iter;
-    Glib::ustring this_text;
-    row.get_value(0, this_text);
-
-    if(this_text == text)
-    {
-      #ifndef GLOM_ENABLE_MAEMO
-      set_active(iter);
-      #else
-      set_selected(iter);
-      #endif //GLOM_ENABLE_MAEMO
-      return; //success
-    }
-  }
-
-  //It's OK to pass "" to this method to unset any items:
-  if(!text.empty())
-  {
-    g_warning("ComboGlom::set_text(): no item found for: %s", text.c_str());
-  }
-
-  //Not found, so mark it as blank:
-  #ifndef GLOM_ENABLE_MAEMO
-  unset_active();
-  #else
-  unselect();
-  #endif
-}
-
 Gnome::Gda::Value ComboGlom::get_value() const
 {
-  sharedptr<const LayoutItem_Field> layout_item = sharedptr<const LayoutItem_Field>::cast_dynamic(get_layout_item());
-  bool success = false;
-
-  const Glib::ustring text = get_text();
-  return Conversions::parse_value(layout_item->get_glom_type(), text, layout_item->get_formatting_used().m_numeric_format, success);
-}
-
-Glib::ustring ComboGlom::get_text() const
-{
-  //Get the active row:
-  #ifndef GLOM_ENABLE_MAEMO
-  Gtk::TreeModel::iterator iter = get_active();
-  #else
-  ComboGlom* unconst = const_cast<ComboGlom*>(this);
-  Gtk::TreeModel::iterator iter = unconst->get_selected();
-  #endif //GLOM_ENABLE_MAEMO
-
-  if(iter)
-  {
-    const Gtk::TreeModel::Row row = *iter;
-    Glib::ustring text;
-    row.get_value(0, text);
-    return text;
+   //Get the active row:
+   #ifndef GLOM_ENABLE_MAEMO
+   Gtk::TreeModel::iterator iter = get_active();
+   #else
+   ComboGlom* unconst = const_cast<ComboGlom*>(this);
+   Gtk::TreeModel::iterator iter = unconst->get_selected();
+   #endif //GLOM_ENABLE_MAEMO
+
+   if(iter)
+   {
+     const Gtk::TreeModel::Row row = *iter;
+     Gnome::Gda::Value value;
+     row.get_value(0, value);
+     return value;
   }
-
-  return Glib::ustring();
+  
+  return Gnome::Gda::Value();
 }
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
diff --git a/glom/mode_data/datawidget/combo.h b/glom/mode_data/datawidget/combo.h
index 0ce94bf..81db53a 100644
--- a/glom/mode_data/datawidget/combo.h
+++ b/glom/mode_data/datawidget/combo.h
@@ -57,14 +57,13 @@ public:
 
   virtual ~ComboGlom();
 
-  virtual void set_read_only(bool read_only = true);
-
+  //This creates a simple ListStore, with a text cell renderer.
+  virtual void set_choices_fixed(const FieldFormatting::type_list_values& list_values);
 
-  //Override this so we can store the text to compare later.
-  //This is not virtual, so you must not use it via Gtk::Entry.
-  void set_text(const Glib::ustring& text); //override
+  //This creates a db-based tree model, with appropriate cell renderers:
+  virtual void set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value);
 
-  Glib::ustring get_text() const;
+  virtual void set_read_only(bool read_only = true);
 
   /** Set the text from a Gnome::Gda::Value.
    */
@@ -74,7 +73,6 @@ public:
 
 private:
   void init();
-  virtual void use_model();
 
   #ifndef GLOM_ENABLE_MAEMO
   // Note that this is a normal signal handler when glibmm was complied
@@ -93,7 +91,7 @@ private:
   virtual Application* get_application();
 
 
-  Glib::ustring m_old_text;
+  Gnome::Gda::Value m_old_value; //TODO: Only useful for navigation, which currently has no implementation.
   //Gnome::Gda::Value m_value; //The last-stored value. We have this because the displayed value might be unparseable.
 
   #ifdef GLOM_ENABLE_MAEMO
diff --git a/glom/mode_data/datawidget/combo_as_radio_buttons.cc b/glom/mode_data/datawidget/combo_as_radio_buttons.cc
index de781dd..84be03c 100644
--- a/glom/mode_data/datawidget/combo_as_radio_buttons.cc
+++ b/glom/mode_data/datawidget/combo_as_radio_buttons.cc
@@ -100,7 +100,7 @@ void ComboAsRadioButtons::set_choices_with_second(const type_list_values_with_se
 
             title += " - " + value_second; //TODO: Find a better way to join them?
           }
-          
+
           ++iterValues;
         }
       }
@@ -149,7 +149,12 @@ void ComboAsRadioButtons::set_choices_fixed(const FieldFormatting::type_list_val
   }
 }
 
-
+void ComboAsRadioButtons::set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value)
+{
+  const Utils::type_list_values_with_second list_values =
+    Utils::get_choice_values(document, layout_field, foreign_key_value);
+  set_choices_with_second(list_values);
+}
 
 ComboAsRadioButtons::~ComboAsRadioButtons()
 {
diff --git a/glom/mode_data/datawidget/combo_as_radio_buttons.h b/glom/mode_data/datawidget/combo_as_radio_buttons.h
index fd26438..23e8e17 100644
--- a/glom/mode_data/datawidget/combo_as_radio_buttons.h
+++ b/glom/mode_data/datawidget/combo_as_radio_buttons.h
@@ -37,7 +37,7 @@ namespace DataWidgetChildren
  * Use this only when the user should only be allowed to enter values that are in the choices.
  */
 class ComboAsRadioButtons
-: 
+:
   public Gtk::VBox,
   public ComboChoices
 {
@@ -47,11 +47,12 @@ public:
   ComboAsRadioButtons();
 
   virtual ~ComboAsRadioButtons();
-  
+
   virtual void set_choices_fixed(const FieldFormatting::type_list_values& list_values);
 
-  virtual void set_read_only(bool read_only = true);
+  virtual void set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value);
 
+  virtual void set_read_only(bool read_only = true);
 
   //Override this so we can store the text to compare later.
   //This is not virtual, so you must not use it via Gtk::Entry.
@@ -68,7 +69,11 @@ public:
 private:
   void init();
 
-  virtual void set_choices_with_second(const type_list_values_with_second& list_values);
+  typedef std::list<Gnome::Gda::Value> type_list_values;
+  typedef std::list< std::pair<Gnome::Gda::Value, type_list_values> > type_list_values_with_second;
+
+  //A utility function that's needed because we don't use a real db model:
+  void set_choices_with_second(const type_list_values_with_second& list_values);
 
   void on_radiobutton_toggled();
 
@@ -94,4 +99,3 @@ private:
 } //namespace Glom
 
 #endif //GLOM_UTILITY_WIDGETS_COMBOENTRY_GLOM_H
-
diff --git a/glom/mode_data/datawidget/combochoices.cc b/glom/mode_data/datawidget/combochoices.cc
index b77420e..c4b8b1f 100644
--- a/glom/mode_data/datawidget/combochoices.cc
+++ b/glom/mode_data/datawidget/combochoices.cc
@@ -50,8 +50,9 @@ ComboChoices::~ComboChoices()
 {
 }
 
-bool ComboChoices::refresh_data_from_database_with_foreign_key(const Document* document, const Gnome::Gda::Value& foreign_key_value)
+bool ComboChoices::refresh_data_from_database_with_foreign_key(const Document* /* document */, const Gnome::Gda::Value& /* foreign_key_value */)
 {
+  /** TODO:
   sharedptr<LayoutItem_Field> layout_item =
     sharedptr<LayoutItem_Field>::cast_dynamic(get_layout_item());
 
@@ -68,11 +69,13 @@ bool ComboChoices::refresh_data_from_database_with_foreign_key(const Document* d
   set_choices_with_second(list_values);
   set_value(old_value); //Try to preserve the value, even in iter-based ComboBoxes.
 
+  */
   return true;
 }
 
-void ComboChoices::set_choices_related(const Document* document)
+void ComboChoices::set_choices_related(const Document* /* document */, const sharedptr<const LayoutItem_Field>& /* layout_field */, const Gnome::Gda::Value& /* foreign_key_value */)
 {
+  /* TODO:
   type_list_values_with_second list_values;
 
   sharedptr<LayoutItem_Field> layout_item =
@@ -94,6 +97,7 @@ void ComboChoices::set_choices_related(const Document* document)
   const Gnome::Gda::Value old_value = get_value();
   set_choices_with_second(list_values);
   set_value(old_value); //Try to preserve the value, even in iter-based ComboBoxes.
+  */
 }
 
 } //namespace DataWidgetChildren
diff --git a/glom/mode_data/datawidget/combochoices.h b/glom/mode_data/datawidget/combochoices.h
index 8fc68f9..948283b 100644
--- a/glom/mode_data/datawidget/combochoices.h
+++ b/glom/mode_data/datawidget/combochoices.h
@@ -51,14 +51,15 @@ public:
    */
   virtual void set_choices_fixed(const FieldFormatting::type_list_values& list_values) = 0;
 
-  //This is not pure virtual, so some widgets can (temporarily) use a default inefficient implementation.
   /** Show the list of related chocie values based on the LayoutItem's formatting choices.
    * You should first call set_layout_item() to provide that formatting detail,
    * so the widget knows what choices to show, and how to format them.
    *
+   * The LayoutItem_Fields should already have their full field details
+   *
    * See also refresh_data_from_database_with_foreign_key().
    */
-  virtual void set_choices_related(const Document* document);
+  virtual void set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value) = 0;
 
   /** Update a choices widget's list of related choices if a relevant value in its parent table has changed.
    *
@@ -69,10 +70,6 @@ public:
 protected:
   void init();
 
-  typedef std::list<Gnome::Gda::Value> type_list_values;
-  typedef std::list< std::pair<Gnome::Gda::Value, type_list_values> > type_list_values_with_second;
-  virtual void set_choices_with_second(const type_list_values_with_second& list_values) = 0;
-
   //Gnome::Gda::Value m_value; //The last-stored value. We have this because the displayed value might be unparseable.
 };
 
diff --git a/glom/mode_data/datawidget/combochoiceswithtreemodel.cc b/glom/mode_data/datawidget/combochoiceswithtreemodel.cc
index 6007132..cc4dff9 100644
--- a/glom/mode_data/datawidget/combochoiceswithtreemodel.cc
+++ b/glom/mode_data/datawidget/combochoiceswithtreemodel.cc
@@ -19,7 +19,9 @@
  */
 
 #include "combochoiceswithtreemodel.h"
+#include <glom/mode_data/datawidget/treemodel_db.h>
 #include <libglom/data_structure/glomconversions.h>
+#include <glom/utils_ui.h>
 #include <glibmm/i18n.h>
 //#include <sstream> //For stringstream
 
@@ -35,6 +37,7 @@ namespace DataWidgetChildren
 {
 
 ComboChoicesWithTreeModel::ComboChoicesWithTreeModel()
+: m_fixed_cell_height(0)
 {
   init();
 }
@@ -49,7 +52,7 @@ void ComboChoicesWithTreeModel::init()
   ComboChoices::init();
 }
 
-void ComboChoicesWithTreeModel::create_model(guint columns_count)
+void ComboChoicesWithTreeModel::create_model_non_db(guint columns_count)
 {
   delete_model();
 
@@ -69,9 +72,6 @@ void ComboChoicesWithTreeModel::create_model(guint columns_count)
 
   //Create the model:
   m_refModel = Gtk::ListStore::create(record);
-  
-  //Call the derived class's (virtual) implementation of this:
-  use_model();
 }
 
 void ComboChoicesWithTreeModel::delete_model()
@@ -88,6 +88,7 @@ void ComboChoicesWithTreeModel::delete_model()
   m_refModel.reset();
 }
 
+/*
 void ComboChoicesWithTreeModel::set_choices_with_second(const type_list_values_with_second& list_values)
 {
   //Recreate the entire model:
@@ -124,7 +125,7 @@ void ComboChoicesWithTreeModel::set_choices_with_second(const type_list_values_w
     std::cerr << G_STRFUNC << ": list_store is null." << std::endl;
     return;
   }
-  
+
   for(type_list_values_with_second::const_iterator iter = list_values.begin(); iter != list_values.end(); ++iter)
   {
     Gtk::TreeModel::iterator iterTree = list_store->append();
@@ -167,11 +168,12 @@ void ComboChoicesWithTreeModel::set_choices_with_second(const type_list_values_w
     }
   }
 }
+*/
 
 
 void ComboChoicesWithTreeModel::set_choices_fixed(const FieldFormatting::type_list_values& list_values)
 {
-  create_model(1);
+  create_model_non_db(1); //Use a regular ListStore without a dynamic column?
 
   Glib::RefPtr<Gtk::ListStore> list_store = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(m_refModel);
   if(!list_store)
@@ -179,7 +181,7 @@ void ComboChoicesWithTreeModel::set_choices_fixed(const FieldFormatting::type_li
     std::cerr << G_STRFUNC << ": list_store is null." << std::endl;
     return;
   }
-  
+
   for(FieldFormatting::type_list_values::const_iterator iter = list_values.begin(); iter != list_values.end(); ++iter)
   {
     Gtk::TreeModel::iterator iterTree = list_store->append();
@@ -193,6 +195,52 @@ void ComboChoicesWithTreeModel::set_choices_fixed(const FieldFormatting::type_li
       row.set_value(0, text);
     }
   }
+
+  //The derived class's (virtual) implementation calls this base method and
+  //then sets up the view, using the model.
+}
+
+void ComboChoicesWithTreeModel::set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value)
+{
+  const FieldFormatting& format = layout_field->get_formatting_used();
+  sharedptr<const Relationship> choice_relationship;
+  sharedptr<const LayoutItem_Field> layout_choice_first;
+  sharedptr<const LayoutGroup> layout_choice_extra;
+  bool choice_show_all = false;
+  format.get_choices_related(choice_relationship, layout_choice_first, layout_choice_extra, choice_show_all);
+
+  //Get the list of fields to show:
+  LayoutGroup::type_list_const_items extra_fields;
+  if(layout_choice_extra)
+    extra_fields = layout_choice_extra->get_items_recursive();
+
+  LayoutGroup::type_list_const_items layout_items;
+  layout_items.push_back(layout_choice_first);
+  layout_items.insert(layout_items.end(), extra_fields.begin(), extra_fields.end());
+
+  //Build the FoundSet:
+  const Glib::ustring to_table = choice_relationship->get_to_table();
+  FoundSet found_set;
+  found_set.m_table_name = to_table;
+
+  if(!foreign_key_value.is_null())
+  {
+    const sharedptr<const Field> to_field = document->get_field(to_table, choice_relationship->get_to_field());
+
+    found_set.m_where_clause = Utils::build_simple_where_expression(
+      to_table, to_field, foreign_key_value);
+  }
+
+
+  m_db_layout_items.clear();
+  m_refModel = DbTreeModel::create_from_items(found_set, layout_items, true /* allow_view */, false /* find mode */, m_db_layout_items);
+  if(!m_refModel)
+  {
+    std::cerr << G_STRFUNC << ": DbTreeModel::create_from_items() returned a null model." << std::endl;
+  }
+
+  //The derived class's (virtual) implementation calls this base method and
+  //then sets up the view, using the model.
 }
 
 Glib::RefPtr<Gtk::TreeModel> ComboChoicesWithTreeModel::get_choices_model()
@@ -200,5 +248,145 @@ Glib::RefPtr<Gtk::TreeModel> ComboChoicesWithTreeModel::get_choices_model()
   return m_refModel;
 }
 
+void ComboChoicesWithTreeModel::on_cell_data(const Gtk::TreeModel::iterator& iter, Gtk::CellRenderer* cell, guint model_column_index)
+{
+  //std::cout << G_STRFUNC << ": DEBUG: model_column_index=" << model_column_index << std::endl;
+  if(model_column_index >= m_db_layout_items.size())
+  {
+    std::cerr << G_STRFUNC << ": model_column_index is out of range." << std::endl;
+    return;
+  }
+   
+  if(!cell)
+  {
+    std::cerr << G_STRFUNC << ": cell is null." << std::endl;
+    return;
+  }
+
+  if(iter)
+  {
+    const sharedptr<const LayoutItem>& layout_item = m_db_layout_items[model_column_index];
+
+    sharedptr<const LayoutItem_Field> field = sharedptr<const LayoutItem_Field>::cast_dynamic(layout_item);
+    if(field)
+    {
+      Gtk::TreeModel::Row treerow = *iter;
+      Gnome::Gda::Value value;
+      treerow->get_value(model_column_index, value);
+
+      const Field::glom_field_type type = field->get_glom_type();
+      switch(type)
+      {
+        case(Field::TYPE_BOOLEAN):
+        {
+          Gtk::CellRendererToggle* pDerived = dynamic_cast<Gtk::CellRendererToggle*>(cell);
+          if(pDerived)
+            pDerived->set_active( (value.get_value_type() == G_TYPE_BOOLEAN) && value.get_boolean() );
+
+          break;
+        }
+        case(Field::TYPE_IMAGE):
+        {
+          Gtk::CellRendererPixbuf* pDerived = dynamic_cast<Gtk::CellRendererPixbuf*>(cell);
+          if(pDerived)
+          {
+            const Glib::RefPtr<Gdk::Pixbuf> pixbuf = Utils::get_pixbuf_for_gda_value(value);
+
+            //Scale it down to a sensible size.
+            //TODO: if(pixbuf)
+            //  pixbuf = Utils::image_scale_keeping_ratio(pixbuf,  get_fixed_cell_height(), pixbuf->get_width());
+            g_object_set(pDerived->gobj(), "pixbuf", pixbuf ? pixbuf->gobj() : 0, (gpointer)0);
+          }
+          else
+            std::cerr << "Field::sql(): glom_type is TYPE_IMAGE but gda type is not VALUE_TYPE_BINARY" << std::endl;
+
+          break;
+        }
+        default:
+        {
+          //TODO: Maybe we should have custom cellcells for time, date, and numbers.
+          Gtk::CellRendererText* pDerived = dynamic_cast<Gtk::CellRendererText*>(cell);
+          if(pDerived)
+          {
+            //std::cout << "debug: " << G_STRFUNC << ": field name=" << field->get_name() << ", glom type=" << field->get_glom_type() << std::endl;
+            const Glib::ustring text = Conversions::get_text_for_gda_value(field->get_glom_type(), value, field->get_formatting_used().m_numeric_format);
+            pDerived->property_text() = text;
+          }
+          else
+          {
+             std::cerr << G_STRFUNC << ": cell has an unexpected type: " << typeid(cell).name() << std::endl;
+          }
+
+          //Show a different color if the value is numeric, if that's specified:
+          if(type == Field::TYPE_NUMERIC)
+          {
+             const Glib::ustring fg_color =
+               field->get_formatting_used().get_text_format_color_foreground_to_use(value);
+             if(!fg_color.empty())
+                 g_object_set(pDerived->gobj(), "foreground", fg_color.c_str(), (gpointer)0);
+             else
+                 g_object_set(pDerived->gobj(), "foreground", (const char*)0, (gpointer)0);
+          }
+
+          break;
+        }
+      }
+    }
+  }
+}
+
+void ComboChoicesWithTreeModel::cell_connect_cell_data_func(Gtk::CellLayout* celllayout, Gtk::CellRenderer* cell, guint model_column_index)
+{
+  celllayout->set_cell_data_func(*cell,
+    sigc::bind( sigc::mem_fun(*this, &ComboChoicesWithTreeModel::on_cell_data), cell, model_column_index));
+}
+
+int ComboChoicesWithTreeModel::get_fixed_cell_height(Gtk::Widget& widget)
+{
+  if(m_fixed_cell_height <= 0)
+  {
+    // Discover a suitable height, and cache it,
+    // by looking at the heights of all columns:
+    // Note that this is usually calculated during construct_specified_columns(),
+    // when all columns are known.
+
+    //Get a default:
+    const Glib::RefPtr<const Pango::Layout> refLayout = widget.create_pango_layout("example");
+    int width = 0;
+    int height = 0;
+    refLayout->get_pixel_size(width, height);
+    m_fixed_cell_height = height;
+
+    //Look at each column:
+    for(type_vec_const_layout_items::iterator iter = m_db_layout_items.begin(); iter != m_db_layout_items.end(); ++iter)
+    {
+      Glib::ustring font_name;
+
+      const sharedptr<const LayoutItem_WithFormatting> item_withformatting = sharedptr<const LayoutItem_WithFormatting>::cast_dynamic(*iter);
+      if(item_withformatting)
+      {
+         const FieldFormatting& formatting = item_withformatting->get_formatting_used();
+         font_name = formatting.get_text_format_font();
+      }
+
+      if(font_name.empty())
+        continue;
+
+      // Translators: This is just some example text used to discover an appropriate height for user-entered text in the UI. This text itself is never shown to the user.
+      Glib::RefPtr<Pango::Layout> refLayout = widget.create_pango_layout(_("Example"));
+      const Pango::FontDescription font(font_name);
+      refLayout->set_font_description(font);
+      int width = 0;
+      int height = 0;
+      refLayout->get_pixel_size(width, height);
+
+      if(height > m_fixed_cell_height)
+        m_fixed_cell_height = height;
+    }
+  }
+
+  return m_fixed_cell_height;
+}
+
 } //namespace DataWidetChildren
 } //namespace Glom
diff --git a/glom/mode_data/datawidget/combochoiceswithtreemodel.h b/glom/mode_data/datawidget/combochoiceswithtreemodel.h
index 750389a..bbfdcd4 100644
--- a/glom/mode_data/datawidget/combochoiceswithtreemodel.h
+++ b/glom/mode_data/datawidget/combochoiceswithtreemodel.h
@@ -37,30 +37,45 @@ public:
 
   virtual ~ComboChoicesWithTreeModel();
 
+  //This creates a simple ListStore, with a text cell renderer.
   virtual void set_choices_fixed(const FieldFormatting::type_list_values& list_values);
 
+  //This creates a db-based tree model, with appropriate cell renderers:
+  virtual void set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value);
+
+
   //Not named get_model(), to avoid clashing with ComboBox::get_model().
   Glib::RefPtr<Gtk::TreeModel> get_choices_model();
-  
+
 protected:
   void init();
-  void create_model(guint columns_count);
-  
-  /** Derived classes should implement this to present the model in their view,
-   * for instance by adding Gtk::CellRenderers.
-   */
-  virtual void use_model() = 0;
-
-  virtual void set_choices_with_second(const type_list_values_with_second& list_values);
+  void create_model_non_db(guint columns_count);
 
+  /** Get a suitable fixed height for cells, so we can display them more efficiently.
+   * This caches the result to avoid repeated recalculation.
+   */
+  int get_fixed_cell_height(Gtk::Widget& widget);
+  
   typedef Gtk::TreeModelColumn<Glib::ustring> type_model_column;
   typedef std::vector< type_model_column* > type_vec_model_columns;
   type_vec_model_columns m_vec_model_columns;
 
+  typedef std::vector< sharedptr<const LayoutItem_Field> > type_vec_const_layout_items;
+  type_vec_const_layout_items m_db_layout_items; //If set_choices_related() was used.
+
+  //This avoids us making on_cell_data() public just so that derived classes can use it,
+  //though that shouldn't be necessary anyway.
+  void cell_connect_cell_data_func(Gtk::CellLayout* celllayout, Gtk::CellRenderer* cell, guint model_column_index);
+
 private:
+  /// Render the model data to the cells in the view.
+  void on_cell_data(const Gtk::TreeModel::iterator& iter, Gtk::CellRenderer* cell, guint model_column_index);
+
   Glib::RefPtr<Gtk::TreeModel> m_refModel;
-  
+
   void delete_model();
+  
+  int m_fixed_cell_height;
 };
 
 } //namespace DataWidetChildren
diff --git a/glom/mode_data/datawidget/comboentry.cc b/glom/mode_data/datawidget/comboentry.cc
index fc68d07..590415b 100644
--- a/glom/mode_data/datawidget/comboentry.cc
+++ b/glom/mode_data/datawidget/comboentry.cc
@@ -22,6 +22,7 @@
 #include <libglom/data_structure/glomconversions.h>
 #include <gtkmm/messagedialog.h>
 #include <glom/dialog_invalid_data.h>
+#include <glom/mode_data/datawidget/cellcreation.h>
 #include <libglom/data_structure/glomconversions.h>
 #include <glom/application.h>
 #include <glibmm/i18n.h>
@@ -108,10 +109,19 @@ ComboEntry::~ComboEntry()
 {
 }
 
-void ComboEntry::use_model()
+
+void ComboEntry::set_choices_fixed(const FieldFormatting::type_list_values& list_values)
 {
+  ComboChoicesWithTreeModel::set_choices_fixed(list_values);
+
   //Show model in the view:
   Glib::RefPtr<Gtk::TreeModel> model = get_choices_model();
+  if(!model)
+  {
+    std::cerr << G_STRFUNC << ": model is null." << std::endl;
+    return;
+  }
+
   set_model(model);
   set_text_column(0);
 
@@ -123,7 +133,7 @@ void ComboEntry::use_model()
     {
       //Get the default column, created by set_text_column():
       cell = dynamic_cast<Gtk::CellRendererText*>(get_first_cell());
-      
+
       //Unpack and repack it with expand=false instead of expand=true:
       //We don't expand the first column, so we can align the other columns.
       cell->reference();
@@ -140,11 +150,61 @@ void ComboEntry::use_model()
 
     //Make the renderer render the column:
     add_attribute(*cell, "text", i);
-    
+
     cell->property_xalign() = 0.0f;
   }
 }
 
+void ComboEntry::set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value)
+{
+  ComboChoicesWithTreeModel::set_choices_related(document, layout_field, foreign_key_value);
+
+  Glib::RefPtr<Gtk::TreeModel> model = get_choices_model();
+  if(!model)
+  {
+    std::cerr << G_STRFUNC << ": model is null." << std::endl;
+    return;
+  }
+
+  //Show the model in the view:
+  set_model(model);
+  //clear() breaks GtkComboBoxEntry. TODO: Fix the C code? clear();
+  set_text_column(0); //TODO: Add a virtual model to TreeModelDb so we always have a text model?
+
+  const guint columns_count = model->get_n_columns();
+  for(guint i = 0; i < columns_count; ++i)
+  for(type_vec_const_layout_items::const_iterator iter = m_db_layout_items.begin(); iter != m_db_layout_items.end(); ++iter)
+  {
+    const sharedptr<const LayoutItem> layout_item = *iter;
+    Gtk::CellRenderer* cell = 0;
+
+    if(i == 0)
+    {
+      //Get the default column, created by set_text_column():
+      cell = get_first_cell();
+      if(!cell)
+        std::cerr << G_STRFUNC << ": get_first_cell() returned null." << std::endl;
+      else
+      {
+        //Unpack and repack it with expand=false instead of expand=true:
+        //We don't expand the first column, so we can align the other columns.
+        cell->reference();
+        clear();
+        pack_start(*cell, false);
+        cell->unreference();
+      }
+    }
+    else
+    {
+      //Create the cell:
+      cell = create_cell(layout_item, m_table_name, document, get_fixed_cell_height(*this));
+      pack_start(*cell, true);
+      
+      cell_connect_cell_data_func(this, cell, i);
+    }
+  }
+}
+
 void ComboEntry::set_layout_item(const sharedptr<LayoutItem>& layout_item, const Glib::ustring& table_name)
 {
   //Call base class:
@@ -183,7 +243,7 @@ void ComboEntry::check_for_change()
     get_entry()->set_text(m_old_text);
   }
 
-  Glib::ustring new_text = get_entry()->get_text();
+  const Glib::ustring new_text = get_entry()->get_text();
   if(new_text != m_old_text)
   {
     //Validate the input:
diff --git a/glom/mode_data/datawidget/comboentry.h b/glom/mode_data/datawidget/comboentry.h
index b151c12..b758601 100644
--- a/glom/mode_data/datawidget/comboentry.h
+++ b/glom/mode_data/datawidget/comboentry.h
@@ -57,6 +57,12 @@ public:
 
   virtual ~ComboEntry();
 
+  //This creates a simple ListStore, with a text cell renderer.
+  virtual void set_choices_fixed(const FieldFormatting::type_list_values& list_values);
+
+  //This creates a db-based tree model, with appropriate cell renderers:
+  virtual void set_choices_related(const Document* document, const sharedptr<const LayoutItem_Field>& layout_field, const Gnome::Gda::Value& foreign_key_value);
+
   //Override this so we can store the text to compare later.
   //This is not virtual, so you must not use it via Gtk::Entry.
   void set_text(const Glib::ustring& text); //override
@@ -73,8 +79,6 @@ public:
 
 private:
   void init();
-  virtual void use_model();
-
 
   //Overrides of default signal handlers:
   //TODO: Are these really default signal handlers?
diff --git a/glom/mode_data/datawidget/datawidget.cc b/glom/mode_data/datawidget/datawidget.cc
index 82e17d6..404f37f 100644
--- a/glom/mode_data/datawidget/datawidget.cc
+++ b/glom/mode_data/datawidget/datawidget.cc
@@ -130,7 +130,7 @@ DataWidget::DataWidget(const sharedptr<LayoutItem_Field>& field, const Glib::ust
         combo = create_combo_widget_for_field(field);
         combo->set_layout_item( get_layout_item(), table_name);
 
-        combo->set_choices_related(document);
+        combo->set_choices_related(document, field, Gnome::Gda::Value() /* TODO: Doesn't make sense */);
       }
       else
       {
diff --git a/glom/mode_data/db_adddel/treemodel_db.cc b/glom/mode_data/datawidget/treemodel_db.cc
similarity index 89%
rename from glom/mode_data/db_adddel/treemodel_db.cc
rename to glom/mode_data/datawidget/treemodel_db.cc
index e834657..ba47239 100644
--- a/glom/mode_data/db_adddel/treemodel_db.cc
+++ b/glom/mode_data/datawidget/treemodel_db.cc
@@ -203,12 +203,113 @@ DbTreeModel::~DbTreeModel()
   clear();
 }
 
-//static:
+
 Glib::RefPtr<DbTreeModel> DbTreeModel::create(const Gtk::TreeModelColumnRecord& columns, const FoundSet& found_set, const type_vec_const_fields& column_fields, int column_index_key, bool get_records, bool find_mode)
 {
   return Glib::RefPtr<DbTreeModel>( new DbTreeModel(columns, found_set, column_fields, column_index_key, get_records, find_mode) );
 }
 
+Glib::RefPtr<DbTreeModel> DbTreeModel::create_from_items(const FoundSet& found_set, const type_vec_layout_items& layout_items, bool get_records, bool find_mode, Base_DB::type_vecConstLayoutFields& fields_shown)
+{
+  //Create a const version of the input, because C++ can't convert it automatically:
+  type_vec_const_layout_items const_items;
+  const_items.insert(const_items.end(), layout_items.begin(), layout_items.end());
+
+  return create_from_items(found_set, const_items, get_records, find_mode, fields_shown);
+}
+
+Glib::RefPtr<DbTreeModel> DbTreeModel::create_from_items(const FoundSet& found_set, const type_vec_const_layout_items& layout_items, bool get_records, bool find_mode, Base_DB::type_vecConstLayoutFields& fields_shown)
+{
+  Glib::RefPtr<DbTreeModel> result;
+
+  typedef Gtk::TreeModelColumn<Gnome::Gda::Value> type_modelcolumn_value;
+  typedef std::vector< type_modelcolumn_value* > type_vecModelColumns;
+  type_vecModelColumns vecModelColumns(layout_items.size(), 0);
+
+  //Create the Gtk ColumnRecord:
+
+  Gtk::TreeModel::ColumnRecord record;
+
+  //Database columns:
+  DbTreeModel::type_vec_const_fields fields;
+  {
+    type_vecModelColumns::size_type i = 0;
+    for(type_vec_const_layout_items::const_iterator iter = layout_items.begin(); iter != layout_items.end(); ++iter)
+    {
+      sharedptr<const LayoutItem_Field> item_field = sharedptr<const LayoutItem_Field>::cast_dynamic(*iter);
+      if(item_field)
+      {
+        type_modelcolumn_value* pModelColumn = new type_modelcolumn_value;
+
+        //Store it so we can use it and delete it later:
+        vecModelColumns[i] = pModelColumn;
+
+        record.add( *pModelColumn );
+
+        fields.push_back(item_field);
+
+        i++;
+      }
+    }
+  }
+
+  fields_shown = fields;
+
+  {
+    //Find the primary key:
+    int column_index_key = 0;
+    bool key_found = false;
+    for( DbTreeModel::type_vec_const_fields::const_iterator iter = fields.begin(); iter != fields.end(); ++iter)
+    {
+      const sharedptr<const LayoutItem_Field> layout_item = *iter;
+      if(!layout_item)
+        continue;
+
+      if( !(layout_item->get_has_relationship_name()) )
+      {
+        sharedptr<const Field> field_full = layout_item->get_full_field_details();
+        if(!field_full)
+          std::cerr << G_STRFUNC << ": The layout item (" << layout_item->get_name() << ") has no field details." << std::endl;
+        else if(field_full->get_primary_key() )
+        {
+          key_found = true;
+          break;
+        }
+      }
+
+      ++column_index_key;
+    }
+
+    if(key_found)
+    {
+      //Create the model from the ColumnRecord:
+      //Note that the model will use a dummy Gda DataModel if m_find_mode is true.
+      //std::cout << "debug: Creating new DbTreeModel() for table=" << m_found_set.m_table_name << std::endl;
+      result =  DbTreeModel::create(record, found_set, fields, column_index_key, get_records, find_mode);
+    }
+    else
+    {
+      std::cerr << G_STRFUNC << ": no primary key field found in the list of items:" << std::endl;
+      for(DbTreeModel::type_vec_const_fields::const_iterator iter = fields.begin(); iter != fields.end(); ++iter)
+      {
+        const sharedptr<const LayoutItem_Field> layout_item = *iter;
+        if(layout_item)
+          std::cerr << "  field: " << layout_item->get_name() << std::endl;
+      }
+    }
+  }
+
+  //Delete the vector's items:
+  for(type_vecModelColumns::iterator iter = vecModelColumns.begin(); iter != vecModelColumns.end(); ++iter)
+  {
+     type_modelcolumn_value* pModelColumn = *iter;
+     if(pModelColumn)
+       delete pModelColumn;
+  }
+
+  return result;
+}
+
 bool DbTreeModel::refresh_from_database(const FoundSet& found_set)
 {
   //std::cout << "DbTreeModel::refresh_from_database()" << std::endl;
diff --git a/glom/mode_data/db_adddel/treemodel_db.h b/glom/mode_data/datawidget/treemodel_db.h
similarity index 87%
rename from glom/mode_data/db_adddel/treemodel_db.h
rename to glom/mode_data/datawidget/treemodel_db.h
index f048ce9..3fbfcba 100644
--- a/glom/mode_data/db_adddel/treemodel_db.h
+++ b/glom/mode_data/datawidget/treemodel_db.h
@@ -73,13 +73,32 @@ public:
   friend class DbTreeModelRow;
 
 private:
-  //Create a TreeModel with @a columns_count number of columns, each of type Glib::ustring.
+
   DbTreeModel(const Gtk::TreeModelColumnRecord& columns, const FoundSet& found_set, const type_vec_const_fields& column_fields, int column_index_key, bool get_records = true, bool find_mode = false);
   virtual ~DbTreeModel();
 
-public:
+ /** Create a new model, using the specified fields.
+   * The LayoutItem_Fields should already have their full field details.
+   */
   static Glib::RefPtr<DbTreeModel> create(const Gtk::TreeModelColumnRecord& columns, const FoundSet& found_set, const type_vec_const_fields& column_fields, int column_index_key, bool get_records = true, bool find_mode = false);
 
+public:
+
+  typedef std::vector< sharedptr<LayoutItem> > type_vec_layout_items;
+  typedef std::vector< sharedptr<const LayoutItem> > type_vec_const_layout_items;
+
+
+  /** A convenience method, creating the model from a list of LayoutItems,
+   * instead of a list of LayoutItem_Fields.
+   */
+  static Glib::RefPtr<DbTreeModel> create_from_items(const FoundSet& found_set, const type_vec_layout_items& layout_items, bool get_records, bool find_mode, Base_DB::type_vecConstLayoutFields& fields_shown);
+
+  /** A convenience method, creating the model from a list of LayoutItems,
+   * instead of a list of LayoutItem_Fields.
+   * Any LayoutItem_Fields should already have their full field details.
+   */
+  static Glib::RefPtr<DbTreeModel> create_from_items(const FoundSet& found_set, const type_vec_const_layout_items& layout_items, bool get_records, bool find_mode, Base_DB::type_vecConstLayoutFields& fields_shown);
+
   typedef DbTreeModelRow::DbValue DbValue;
 
   void set_is_not_placeholder(const TreeModel::iterator& iter);
diff --git a/glom/mode_data/db_adddel/db_adddel.cc b/glom/mode_data/db_adddel/db_adddel.cc
index 18f2c77..dfe5e18 100644
--- a/glom/mode_data/db_adddel/db_adddel.cc
+++ b/glom/mode_data/db_adddel/db_adddel.cc
@@ -672,7 +672,7 @@ Gtk::CellRenderer* DbAddDel::construct_specified_columns_cellrenderer(const shar
         #ifndef GLOM_ENABLE_MAEMO //There's no direct editing via the list view on Maemo.
         //Connect to its signal:
         pCellRendererToggle->signal_toggled().connect(
-        sigc::bind( sigc::mem_fun(*this, &DbAddDel::on_treeview_cell_edited_bool), model_column_index, data_model_column_index ) );
+          sigc::bind( sigc::mem_fun(*this, &DbAddDel::on_treeview_cell_edited_bool), model_column_index, data_model_column_index ) );
         #endif //GLOM_ENABLE_MAEMO
       }
     }
@@ -702,127 +702,6 @@ Gtk::CellRenderer* DbAddDel::construct_specified_columns_cellrenderer(const shar
   return pCellRenderer;
 }
 
-typedef std::vector< sharedptr<const LayoutItem> > type_vec_const_layout_items;
-typedef std::vector< sharedptr<LayoutItem> > type_vec_layout_items;
-
-static Glib::RefPtr<DbTreeModel> create_model_db(const FoundSet& found_set, const type_vec_const_layout_items& layout_items, bool get_records, bool find_mode, Base_DB::type_vecConstLayoutFields& fields_shown)
-{
-  Glib::RefPtr<DbTreeModel> result;
-
-  typedef Gtk::TreeModelColumn<Gnome::Gda::Value> type_modelcolumn_value;
-  typedef std::vector< type_modelcolumn_value* > type_vecModelColumns;
-  type_vecModelColumns vecModelColumns(layout_items.size(), 0);
-
-  //Create the Gtk ColumnRecord:
-
-  Gtk::TreeModel::ColumnRecord record;
-
-  //Database columns:
-  Base_DB::type_vecConstLayoutFields fields;
-  {
-    type_vecModelColumns::size_type i = 0;
-    for(type_vec_const_layout_items::const_iterator iter = layout_items.begin(); iter != layout_items.end(); ++iter)
-    {
-      sharedptr<const LayoutItem_Field> item_field = sharedptr<const LayoutItem_Field>::cast_dynamic(*iter);
-      if(item_field)
-      {
-        type_modelcolumn_value* pModelColumn = new type_modelcolumn_value;
-
-        //Store it so we can use it and delete it later:
-        vecModelColumns[i] = pModelColumn;
-
-        record.add( *pModelColumn );
-
-        fields.push_back(item_field);
-
-        i++;
-      }
-    }
-  }
-
-  fields_shown = fields;
-
-  {
-    //Find the primary key:
-    int column_index_key = 0;
-    bool key_found = false;
-    for(Base_DB::type_vecConstLayoutFields::const_iterator iter = fields.begin(); iter != fields.end(); ++iter)
-    {
-      sharedptr<const LayoutItem_Field> layout_item = *iter;
-      if( !(layout_item->get_has_relationship_name()) )
-      {
-        sharedptr<const Field> field_full = layout_item->get_full_field_details();
-        if(field_full && field_full->get_primary_key() )
-        {
-          key_found = true;
-          break;
-        }
-      }
-
-      ++column_index_key;
-    }
-
-    if(key_found)
-    {
-      //Create the model from the ColumnRecord:
-      //Note that the model will use a dummy Gda DataModel if m_find_mode is true.
-      //std::cout << "debug: Creating new DbTreeModel() for table=" << m_found_set.m_table_name << std::endl;
-      result =  DbTreeModel::create(record, found_set, fields, column_index_key, get_records, find_mode);
-    }
-    else
-    {
-      g_warning("%s: no primary key field found.", __FUNCTION__);
-      //for(DbTreeModel::type_vec_const_fields::const_iterator iter = fields.begin(); iter != fields.end(); ++iter)
-      //{
-      //  g_warning("  field: %s", (iter->get_name().c_str());
-      //}
-    }
-  }
-
-  //Delete the vector's items:
-  for(type_vecModelColumns::iterator iter = vecModelColumns.begin(); iter != vecModelColumns.end(); ++iter)
-  {
-     type_modelcolumn_value* pModelColumn = *iter;
-     if(pModelColumn)
-       delete pModelColumn;
-  }
-
-  return result;
-}
-
-/*
-static Glib::RefPtr<DbTreeModel> create_model_db(const FoundSet& found_set, const type_vec_layout_items& layout_items, bool get_records, bool find_mode, Base_DB::type_vecLayoutFields& fields_shown)
-{
-  //Create a const version of the input, because C++ can't convert it automatically:
-  type_vec_const_layout_items const_items;
-  const_items.insert(const_items.end(), layout_items.begin(), layout_items.end());
-
-  Base_DB::type_vecConstLayoutFields fields_shown_const;
-  Glib::RefPtr<DbTreeModel> result = create_model_db(found_set, const_items, get_records, find_mode, fields_shown_const);
-
-  //Create a non-const version of the output parameter,
-  //because we know it just contains copies of layout_items elements, which were non-const:
-  fields_shown.clear();
-  for(Base_DB::type_vecConstLayoutFields::const_iterator iter = fields_shown_const.begin(); iter != fields_shown_const.end(); ++iter)
-  {
-     sharedptr<const LayoutItem_Field> field = *iter;
-     sharedptr<LayoutItem_Field> unconst = sharedptr<LayoutItem_Field>::cast_const(field);
-     fields_shown.push_back(unconst);
-  }
-
-  return result;
-}
-*/
-
-static Glib::RefPtr<DbTreeModel> create_model_db(const FoundSet& found_set, const type_vec_layout_items& layout_items, bool get_records, bool find_mode, Base_DB::type_vecConstLayoutFields& fields_shown)
-{
-  //Create a const version of the input, because C++ can't convert it automatically:
-  type_vec_const_layout_items const_items;
-  const_items.insert(const_items.end(), layout_items.begin(), layout_items.end());
-
-  return create_model_db(found_set, const_items, get_records, find_mode, fields_shown);
-}
-
 void DbAddDel::construct_specified_columns()
 {
   InnerIgnore innerIgnore(this);
@@ -851,7 +730,7 @@ void DbAddDel::construct_specified_columns()
     return;
   }
 
-  m_refListStore = create_model_db(m_found_set, m_column_items, m_allow_view, m_find_mode, m_FieldsShown);
+  m_refListStore = DbTreeModel::create_from_items(m_found_set, m_column_items, m_allow_view, m_find_mode, m_FieldsShown);
   //m_FieldsShown is needed by Base_DB_Table_Data::record_new().
 
   #ifdef GLOM_ENABLE_MAEMO
@@ -1096,9 +975,7 @@ void DbAddDel::refresh_cell_choices_data_from_database_with_foreign_key(guint mo
     return;
   }
 
-  const Utils::type_list_values_with_second list_values =
-    Utils::get_choice_values(get_document(), layout_field, foreign_key_value);
-  cell->set_choices_with_second(list_values);
+  cell->set_choices_related(get_document(), layout_field, foreign_key_value);
 }
 
 void DbAddDel::remove_all_columns()
diff --git a/glom/mode_data/db_adddel/db_adddel.h b/glom/mode_data/db_adddel/db_adddel.h
index 1152531..82e93ef 100644
--- a/glom/mode_data/db_adddel/db_adddel.h
+++ b/glom/mode_data/db_adddel/db_adddel.h
@@ -24,7 +24,7 @@
 #include <gtkmm.h>
 #include <libglom/data_structure/layout/layoutitem_field.h>
 #include <libgdamm.h>
-#include <glom/mode_data/db_adddel/treemodel_db.h>
+#include <glom/mode_data/datawidget/treemodel_db.h>
 #include <libglom/document/document.h>
 #include <glom/base_db_table_data.h>
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b6b0ae4..2769a16 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -118,6 +118,7 @@ glom/mode_design/translation/combobox_locale.cc
 glom/mode_design/translation/dialog_change_language.cc
 glom/mode_design/translation/window_translations.cc
 glom/utility_widgets/adddel/adddel.cc
+glom/mode_data/datawidget/combochoiceswithtreemodel.cc
 glom/mode_data/datawidget/comboentry.cc
 glom/mode_data/datawidget/datawidget.cc
 glom/mode_data/db_adddel/db_adddel.cc



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]