glom r1596 - in trunk/glom: . layout_item_dialogs libglom libglom/data_structure mode_data mode_design mode_design/fields mode_design/print_layouts mode_find navigation utility_widgets/adddel utility_widgets/db_adddel



Author: murrayc
Date: Tue Apr 29 14:32:35 2008
New Revision: 1596
URL: http://svn.gnome.org/viewvc/glom?rev=1596&view=rev

Log:
2008-04-29  Murray Cummin  <murrayc murrayc com>

Large refactoring of the Base_DB_* and Box_DB_* hierarchy to make it 
simpler and less hacky.

* docs/developer_reference/Doxyfile: Added this, so you can do 
doxygen Doxyfile to get some HTML to help understand the code.

* glom/signal_reemitter.h: Added signal_connect_for_reemit_*args(), 
to make it easier to just emit a signal in response to another one, 
for instance to emit up to a parent widget.
* regression_tests/Makefile.am:
* regression_tests/test_signal_reemit.cc: Added a test for this.

* glom/Makefile.am:
* glom/base_db.[h|cc]: Split this, to create:
* glom/base_db_table.[h|cc]
* glom/base_db_table_data.[h|cc]: with some stuff from Box_DB_Table.

* glom/box_db.[h|cc]:  Renamed to
* glom/box_withbuttons.[h|cc] because that's all this is now.

* glom/box_db_table.[h|cc]: Derive from Base_DB_Table, instead of 
implementing so much stuff.

* glom/mode_data/Makefile.am:
* glom/mode_data/box_data.[h|cc]: Derive from Box_WithButtons,
        and Base_DB_Table_Data instead of implementing so much stuff.

* glom/mode_data/box_data_details.[h|cc]
* glom/mode_data/box_data_manyrecords.cc[h|cc]: Added this base class 
for:
* glom/mode_data/box_data_list.[h|cc]
and Box_Data_Portal:

* glom/mode_data/box_data_portal.cc[h|cc]: Added this base class 
for 
* glom/mode_data/box_data_calendar_related.[h|cc]
and
* glom/mode_data/box_data_list_related.[h|cc]

* glom/utility_widgets/db_adddel/db_adddel.[h|cc] Derive from 
Base_DB_Table_Data, to use its implementation, to move lots of 
code for record adding, changing, and deleting into this widget instead 
of having decisions in the parent Box_Data_List_Related and Box_Data_List.
This is simpler.

Added:
   trunk/glom/base_db_table.cc
      - copied, changed from r1588, /trunk/glom/box_db_table.cc
   trunk/glom/base_db_table.h
      - copied, changed from r1588, /trunk/glom/box_db_table.h
   trunk/glom/base_db_table_data.cc
   trunk/glom/base_db_table_data.h
   trunk/glom/box_withbuttons.cc
      - copied, changed from r1588, /trunk/glom/box_db.cc
   trunk/glom/box_withbuttons.h
      - copied, changed from r1588, /trunk/glom/box_db.h
   trunk/glom/mode_data/box_data_manyrecords.cc
      - copied, changed from r1587, /trunk/glom/mode_data/box_data_list.cc
   trunk/glom/mode_data/box_data_manyrecords.h
      - copied, changed from r1587, /trunk/glom/mode_data/box_data_list.h
   trunk/glom/signal_reemitter.h
Removed:
   trunk/glom/box_db.cc
   trunk/glom/box_db.h
Modified:
   trunk/glom/Makefile.am
   trunk/glom/application.cc
   trunk/glom/base_db.cc
   trunk/glom/base_db.h
   trunk/glom/box_db_table.cc
   trunk/glom/box_db_table.h
   trunk/glom/box_reports.cc
   trunk/glom/dialog_connection.cc
   trunk/glom/dialog_database_preferences.cc
   trunk/glom/dialog_glom.cc
   trunk/glom/dialog_glom.h
   trunk/glom/dialog_new_self_hosted_connection.cc
   trunk/glom/glom_postgres.h
   trunk/glom/glom_privs.h
   trunk/glom/layout_item_dialogs/box_formatting.h
   trunk/glom/layout_item_dialogs/dialog_field_layout.h
   trunk/glom/layout_item_dialogs/dialog_field_summary.h
   trunk/glom/layout_item_dialogs/dialog_group_by.h
   trunk/glom/libglom/data_structure/field.cc
   trunk/glom/libglom/test_connectionpool.cc
   trunk/glom/mode_data/Makefile.am
   trunk/glom/mode_data/box_data.cc
   trunk/glom/mode_data/box_data.h
   trunk/glom/mode_data/box_data_calendar_related.cc
   trunk/glom/mode_data/box_data_calendar_related.h
   trunk/glom/mode_data/box_data_details.cc
   trunk/glom/mode_data/box_data_details.h
   trunk/glom/mode_data/box_data_list.cc
   trunk/glom/mode_data/box_data_list.h
   trunk/glom/mode_data/box_data_list_related.cc
   trunk/glom/mode_data/box_data_list_related.h
   trunk/glom/mode_data/box_data_portal.cc
   trunk/glom/mode_data/box_data_portal.h
   trunk/glom/mode_data/dialog_choose_field.h
   trunk/glom/mode_data/dialog_choose_relationship.h
   trunk/glom/mode_data/dialog_layout.h
   trunk/glom/mode_data/flowtablewithfields.cc
   trunk/glom/mode_data/flowtablewithfields.h
   trunk/glom/mode_design/box_db_table_relationships.cc
   trunk/glom/mode_design/box_db_table_relationships.h
   trunk/glom/mode_design/fields/box_db_table_definition.cc
   trunk/glom/mode_design/print_layouts/box_print_layouts.cc
   trunk/glom/mode_design/print_layouts/box_print_layouts.h
   trunk/glom/mode_find/box_data_details_find.cc
   trunk/glom/mode_find/box_data_list_find.cc
   trunk/glom/mode_find/box_data_list_find.h
   trunk/glom/navigation/box_tables.cc
   trunk/glom/navigation/box_tables.h
   trunk/glom/notebook_glom.cc
   trunk/glom/notebook_glom.h
   trunk/glom/utility_widgets/adddel/adddel.h
   trunk/glom/utility_widgets/db_adddel/db_adddel.cc
   trunk/glom/utility_widgets/db_adddel/db_adddel.h
   trunk/glom/utility_widgets/db_adddel/db_adddel_withbuttons.cc

Modified: trunk/glom/Makefile.am
==============================================================================
--- trunk/glom/Makefile.am	(original)
+++ trunk/glom/Makefile.am	Tue Apr 29 14:32:35 2008
@@ -29,7 +29,9 @@
 glom_SOURCES = main.cc \
                application.cc application.h \
                base_db.h base_db.cc \
-               box_db.h box_db.cc \
+               base_db_table.h base_db_table.cc \
+               base_db_table_data.h base_db_table_data.cc \
+               box_withbuttons.h box_withbuttons.cc \
                box_db_table.h box_db_table.cc \
                glom_postgres.h glom_postgres.cc \
                glom_privs.h glom_privs.cc \

Modified: trunk/glom/application.cc
==============================================================================
--- trunk/glom/application.cc	(original)
+++ trunk/glom/application.cc	Tue Apr 29 14:32:35 2008
@@ -983,7 +983,7 @@
         connection_pool->set_port(port_used);
         connection_pool->set_try_other_ports(try_other_ports);
 
-        connection_pool->set_ready_to_connect(this); //Box_DB::connect_to_server() will now attempt the connection-> Shared instances of m_Connection will also be usable.
+        connection_pool->set_ready_to_connect(this); //Box_WithButtons::connect_to_server() will now attempt the connection-> Shared instances of m_Connection will also be usable.
 
         //Attempt to connect to the specified database:
         bool test = false;

Modified: trunk/glom/base_db.cc
==============================================================================
--- trunk/glom/base_db.cc	(original)
+++ trunk/glom/base_db.cc	Tue Apr 29 14:32:35 2008
@@ -107,22 +107,13 @@
   return fill_from_database();
 }
 
-bool Base_DB::refresh_data_from_database()
-{
-  return fill_from_database();
-}
-
+//TODO: Remove this?
 bool Base_DB::fill_from_database()
 {
   //m_AddDel.remove_all();
   return true;
 }
 
-void Base_DB::fill_end()
-{
-  //Call this from the end of fill_from_database() overrides.
-}
-
 //static:
 #ifdef GLIBMM_EXCEPTIONS_ENABLED
 sharedptr<SharedConnection> Base_DB::connect_to_server(Gtk::Window* parent_window)
@@ -2731,5 +2722,4 @@
   return result;
 }
 
-
 } //namespace Glom

Modified: trunk/glom/base_db.h
==============================================================================
--- trunk/glom/base_db.h	(original)
+++ trunk/glom/base_db.h	Tue Apr 29 14:32:35 2008
@@ -23,7 +23,7 @@
 
 #include "config.h" // For GLOM_ENABLE_CLIENT_ONLY
 
-#include "gtkmm.h"
+#include <gtkmm.h>
 
 #include <glom/libglom/document/document_glom.h>
 #include <glom/libglom/connectionpool.h>
@@ -45,7 +45,7 @@
 
 
 /** A base class that is a Bakery View with some database functionality.
-*/
+ */
 class Base_DB :
   public View_Composite_Glom
 {
@@ -56,9 +56,6 @@
   /// Specify the structure of what will be shown, and fill it.
   virtual bool init_db_details();
 
-  /// Specify what actual data will be shown:
-  virtual bool refresh_data_from_database();
-
   /** Returns whether we are in developer mode.
    * Some functionality will be deactivated when not in developer mode.
    */
@@ -127,6 +124,7 @@
   Glib::ustring get_find_where_clause_quick(const Glib::ustring& table_name, const Gnome::Gda::Value& quick_search) const;
 
 
+  //Methods to be overridden by derived classes:
   virtual void set_entered_field_data(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value&  value);
   virtual void set_entered_field_data(const Gtk::TreeModel::iterator& row, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
 
@@ -294,8 +292,10 @@
   bool check_entered_value_for_uniqueness(const Glib::ustring& table_name, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value, Gtk::Window* parent_window);
   bool check_entered_value_for_uniqueness(const Glib::ustring& table_name, const Gtk::TreeModel::iterator& /* row */,  const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value, Gtk::Window* parent_window);
 
+  /** Fill the UI with information (data or structure, depending on the widget).
+   * Overridden by derived widgets to provide implementation.
+   */
   virtual bool fill_from_database();
-  virtual void fill_end(); //Call this from the end of fill_from_database() overrides.
 
   virtual void on_userlevel_changed(AppState::userlevels userlevel);
 

Copied: trunk/glom/base_db_table.cc (from r1588, /trunk/glom/box_db_table.cc)
==============================================================================
--- /trunk/glom/box_db_table.cc	(original)
+++ trunk/glom/base_db_table.cc	Tue Apr 29 14:32:35 2008
@@ -18,33 +18,29 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#include "box_db_table.h"
+#include "base_db_table.h"
 #include <glom/libglom/data_structure/glomconversions.h>
+#include <glom/application.h>
 #include "python_embed/glom_python.h"
 #include <sstream>
 
 namespace Glom
 {
 
-Box_DB_Table::Box_DB_Table()
+Base_DB_Table::Base_DB_Table()
 {
 }
 
-Box_DB_Table::Box_DB_Table(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade)
-: Box_DB(cobject, refGlade)
+Base_DB_Table::~Base_DB_Table()
 {
 }
 
-Box_DB_Table::~Box_DB_Table()
-{
-}
-
-Glib::ustring Box_DB_Table::get_table_name()
+Glib::ustring Base_DB_Table::get_table_name() const
 {
   return m_table_name;
 }
 
-bool Box_DB_Table::init_db_details(const Glib::ustring& table_name)
+bool Base_DB_Table::init_db_details(const Glib::ustring& table_name)
 {
   m_table_name = table_name;
 
@@ -54,169 +50,6 @@
   return fill_from_database();
 }
 
-bool Box_DB_Table::refresh_data_from_database()
-{
-  if(!ConnectionPool::get_instance()->get_ready_to_connect())
-    return false;
-
-  return fill_from_database();
-}
-
-Gnome::Gda::Value Box_DB_Table::get_entered_field_data_field_only(const sharedptr<const Field>& field) const
-{
-  sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-  layout_item->set_full_field_details(field); 
-
-  return get_entered_field_data(layout_item);
-}
-
-Gnome::Gda::Value Box_DB_Table::get_entered_field_data(const sharedptr<const LayoutItem_Field>& /* field */) const
-{
-  //Override this to use Field::set_data() too.
-
-  return Gnome::Gda::Value(); //null
-}
-
-unsigned long Box_DB_Table::get_last_auto_increment_value(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model, const Glib::ustring& /* field_name */)
-{
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window());
-#else
-  std::auto_ptr<ExceptionConnection> error;
-  sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window(), error);
-#endif
-
-  if(sharedconnection)
-  {
-    Glib::RefPtr<Gnome::Gda::Connection> connection = sharedconnection->get_gda_connection();
-    Glib::ustring id = connection->get_last_insert_id(data_model);
-
-    //Convert it to a numeric type:
-    std::stringstream stream;
-    stream << id;
-    unsigned long id_numeric = 0;
-    stream >> id_numeric;
-
-    return id_numeric;
-  }
-  else
-    return 0;
-}
-
-//static:
-/*
-Box_DB_Table::type_vecFields Box_DB_Table::get_fields_for_datamodel(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model)
-{
-  type_vecFields result;
-
-  int columns =  data_model->get_n_columns();
-  for(int i = 0; i < columns; ++i)
-  {
-    Glib::RefPtr<Gnome::Gda::Column> fieldinfo = data_model->describe_column(i);
-    sharedptr<Field> field(new Field());
-    field->set_field_info(fieldinfo); //TODO: Get glom-specific information from document?
-    result.push_back( field );
-  }
-
-  return result;
-}
-*/
-
-Glib::ustring Box_DB_Table::postgres_get_field_definition_for_sql(const Glib::RefPtr<const Gnome::Gda::Column>& field_info)
-{
-  Glib::ustring strResult;
-
-  //Type
-  Glib::ustring strType = "unknowntype";
-
-  //Postgres has a special "serial" datatype. (MySQL uses a numeric type, and has an extra "AUTO_INCREMENT" command)
-  if(false) //disabled for now - see generate_next_auto_increment() //field_info->get_auto_increment())
-  {
-    strType = "serial";
-  }
-  else
-  {
-    ConnectionPool* pConnectionPool = ConnectionPool::get_instance();
-    if(pConnectionPool)
-    {
-      const FieldTypes* pFieldTypes = pConnectionPool->get_field_types();
-      if(pFieldTypes)
-      {
-        const GType fieldType = field_info->get_g_type();
-        strType = pFieldTypes->get_string_name_for_gdavaluetype(fieldType);
-      }
-    }
-  }
-
-  strResult += strType;
-
-   /*
-  //Optinal type details: (M, D), UNSIGNED
-  Field::enumTypeOptionals optionals = fieldType.get_TypeOptionals();
-  if(optionals != Field::TYPE_OPTIONALS_None)
-  {
-  Glib::ustring strOptionals;
-
-  char pchM[10] = {0};
-  sprintf(pchM, "%d", fieldType.get_MaxLength());
-    Glib::ustring strM(pchM);
-
-
-  if(optionals == Field::TYPE_OPTIONALS_M_D)
-  {
-    if( (fieldType.get_MaxLength() != 0) && ( fieldType.get_DecimalsCount() != 0) ) //0 here means use default, so don't specify.
-    {
-      char pchD[10] = {0};
-      sprintf(pchD, "%d", fieldType.get_DecimalsCount());
-      Glib::ustring strD(pchD);
-
-      strOptionals = "(" + strM + "," + strD + ")";
-    }
-
-  }
-  else if(optionals == Field::TYPE_OPTIONALS_M_S)
-  {
-    if( fieldType.get_MaxLength() != 0 ) //0 here means use default, so don't specify.
-      strOptionals = "(" + strM + ")";
-
-      if(!(fieldType.get_Signed()))
-        strOptionals += " UNSIGNED";
-  }
-  else if(optionals == Field::TYPE_OPTIONALS_M)
-  {
-      if( fieldType.get_MaxLength() != 0 ) //0 here means use default, so don't specify.
-      strOptionals = "(" + strM + ")";
-  }
-
-    if(strOptionals.size())
-      strResult += (" " + strOptionals);
-  }
-  */
-
-  //Unique:
-  if(field_info->get_unique_key())
-    strResult += " UNIQUE";
-
-  /* Posgres needs us to do this separately
-  //Not Null:
-  if(!(field_info->get_allow_null()))
-    strResult += " NOT NULL";
-  */
-
-  //Default:
-  Gnome::Gda::Value valueDefault = field_info->get_default_value();
-  const Glib::ustring& strDefault =  valueDefault.to_string();
-  if(!strDefault.empty() && strDefault != "NULL")
-    strResult += " DEFAULT " + strDefault; //TODO: Quote/Escape it if necessary.
-
-  //Primary Key:
-  if(field_info->get_primary_key())
-    strResult += " PRIMARY KEY";
-
-  return strResult;
-}
-
-
 } //namespace Glom
 
 

Copied: trunk/glom/base_db_table.h (from r1588, /trunk/glom/box_db_table.h)
==============================================================================
--- /trunk/glom/box_db_table.h	(original)
+++ trunk/glom/base_db_table.h	Tue Apr 29 14:32:35 2008
@@ -19,49 +19,34 @@
  */
 
 
-#ifndef BOX_DB_TABLE_H
-#define BOX_DB_TABLE_H
+#ifndef BASE_DB_TABLE_H
+#define BASE_DB_TABLE_H
 
-#include "box_db.h"
+#include "base_db.h"
 #include <glom/libglom/data_structure/field.h>
 #include <algorithm> //find_if used in various places.
 
 namespace Glom
 {
 
-class Box_DB_Table : public Box_DB
+/** A base class that is a Bakery View with some database functionality 
+ * for use with a specific database table.
+ */
+class Base_DB_Table : public Base_DB
 {
 public: 
-  Box_DB_Table();
-  Box_DB_Table(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
-  virtual ~Box_DB_Table();
+  Base_DB_Table();
+  virtual ~Base_DB_Table();
 
   virtual bool init_db_details(const Glib::ustring& table_name);
-  virtual bool refresh_data_from_database();
-
-  virtual Glib::ustring get_table_name();
 
-  //TODO: Put this somewhere more sensible:
-  typedef std::map<GType, Glib::ustring> type_map_valuetypes;
+  Glib::ustring get_table_name() const;
 
 protected:
-
-  //virtual Glib::RefPtr<Gnome::Gda::DataModel> record_new(Gnome::Gda::Value primary_key_value);
-
-  Gnome::Gda::Value get_entered_field_data_field_only(const sharedptr<const Field>& field) const;
-  virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
-
-  //static sharedptr<Field> get_field_primary_key(const type_vecFields& fields);
-
-  unsigned long get_last_auto_increment_value(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model, const Glib::ustring& field_name);
-
-
-  //static type_vecFields get_fields_for_datamodel(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model); 
-  static Glib::ustring postgres_get_field_definition_for_sql(const Glib::RefPtr<const Gnome::Gda::Column>& field_info);
-
+    
   Glib::ustring m_table_name;
 };
 
 } //namespace Glom
 
-#endif //BOX_DB_TABLE_H
+#endif //BASE_DB_TABLE_H

Added: trunk/glom/base_db_table_data.cc
==============================================================================
--- (empty file)
+++ trunk/glom/base_db_table_data.cc	Tue Apr 29 14:32:35 2008
@@ -0,0 +1,454 @@
+/* Glom
+ *
+ * Copyright (C) 2001-2004 Murray Cumming
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "base_db_table_data.h"
+#include <glom/libglom/data_structure/glomconversions.h>
+#include <glom/application.h>
+#include "python_embed/glom_python.h"
+#include <sstream>
+#include <glibmm/i18n.h>
+
+namespace Glom
+{
+
+Base_DB_Table_Data::Base_DB_Table_Data()
+{
+}
+
+Base_DB_Table_Data::~Base_DB_Table_Data()
+{
+}
+
+bool Base_DB_Table_Data::refresh_data_from_database()
+{
+  if(!ConnectionPool::get_instance()->get_ready_to_connect())
+    return false;
+
+  return fill_from_database();
+}
+
+Gnome::Gda::Value Base_DB_Table_Data::get_entered_field_data_field_only(const sharedptr<const Field>& field) const
+{
+  sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
+  layout_item->set_full_field_details(field); 
+
+  return get_entered_field_data(layout_item);
+}
+
+Gnome::Gda::Value Base_DB_Table_Data::get_entered_field_data(const sharedptr<const LayoutItem_Field>& /* field */) const
+{
+  //Override this to use Field::set_data() too.
+
+  return Gnome::Gda::Value(); //null
+}
+
+
+Glib::RefPtr<Gnome::Gda::DataModel> Base_DB_Table_Data::record_new(bool use_entered_data, const Gnome::Gda::Value& primary_key_value)
+{
+  sharedptr<const Field> fieldPrimaryKey = get_field_primary_key();
+
+  const Glib::ustring primary_key_name = fieldPrimaryKey->get_name();
+
+  type_vecLayoutFields fieldsToAdd = m_FieldsShown;
+  if(m_TableFields.empty())
+    m_TableFields = get_fields_for_table(m_table_name);
+
+  //Add values for all fields, not just the shown ones:
+  //For instance, we must always add the primary key, and fields with default/calculated/lookup values:
+  for(type_vecFields::const_iterator iter = m_TableFields.begin(); iter != m_TableFields.end(); ++iter)
+  {
+    //TODO: Search for the non-related field with the name, not just the field with the name:
+    type_vecLayoutFields::const_iterator iterFind = std::find_if(fieldsToAdd.begin(), fieldsToAdd.end(), predicate_FieldHasName<LayoutItem_Field>((*iter)->get_name()));
+    if(iterFind == fieldsToAdd.end())
+    {
+      sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
+      layout_item->set_full_field_details(*iter);
+
+      fieldsToAdd.push_back(layout_item);
+    }
+  }
+
+  Document_Glom* document = get_document();
+
+  //Calculate any necessary field values and enter them:
+  for(type_vecLayoutFields::const_iterator iter = fieldsToAdd.begin(); iter != fieldsToAdd.end(); ++iter)
+  {
+    sharedptr<LayoutItem_Field> layout_item = *iter;
+
+    //If the user did not enter something in this field:
+    Gnome::Gda::Value value = get_entered_field_data(layout_item);
+
+    if(Conversions::value_is_empty(value)) //This deals with empty strings too.
+    {
+      const sharedptr<const Field>& field = layout_item->get_full_field_details();
+      if(field)
+      {
+        //If the default value should be calculated, then calculate it:
+        if(field->get_has_calculation())
+        {
+          const Glib::ustring calculation = field->get_calculation();
+          const type_map_fields field_values = get_record_field_values_for_calculation(m_table_name, fieldPrimaryKey, primary_key_value);
+
+          //We need the connection when we run the script, so that the script may use it.
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+          // TODO: Is this function supposed to throw an exception?
+          sharedptr<SharedConnection> sharedconnection = connect_to_server(App_Glom::get_application());
+#else
+          std::auto_ptr<ExceptionConnection> error;
+          sharedptr<SharedConnection> sharedconnection = connect_to_server(App_Glom::get_application(), error);
+          if(error.get() == NULL)
+          {
+            // Don't evaluate function on error
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+
+            const Gnome::Gda::Value value = glom_evaluate_python_function_implementation(field->get_glom_type(), calculation, field_values, document, m_table_name, sharedconnection->get_gda_connection());
+            set_entered_field_data(layout_item, value);
+#ifndef GLIBMM_EXCEPTIONS_ENABLED
+          }
+#endif // !GLIBMM_EXCEPTIONS_ENABLED
+        }
+
+        //Use default values (These are also specified in postgres as part of the field definition,
+        //so we could theoretically just not specify it here.)
+        //TODO_Performance: Add has_default_value()?
+        if(Conversions::value_is_empty(value))
+        {
+          const Gnome::Gda::Value default_value = field->get_default_value();
+          if(!Conversions::value_is_empty(default_value))
+          {
+            set_entered_field_data(layout_item, default_value);
+          }
+        }
+      }
+    }
+  }
+
+  //Get all entered field name/value pairs:
+  Glib::ustring strNames;
+  Glib::ustring strValues;
+
+  //Avoid specifying the same field twice:
+  typedef std::map<Glib::ustring, bool> type_map_added;
+  type_map_added map_added;
+
+  for(type_vecLayoutFields::const_iterator iter = fieldsToAdd.begin(); iter != fieldsToAdd.end(); ++iter)
+  {
+    sharedptr<LayoutItem_Field> layout_item = *iter;
+    const Glib::ustring field_name = layout_item->get_name();
+    if(!layout_item->get_has_relationship_name()) //TODO: Allow people to add a related record also by entering new data in a related field of the related record.
+    {
+      type_map_added::const_iterator iterFind = map_added.find(field_name);
+      if(iterFind == map_added.end()) //If it was not added already
+      {
+        Gnome::Gda::Value value;
+
+        const sharedptr<const Field>& field = layout_item->get_full_field_details();
+        if(field)
+        {
+          //Use the specified (generated) primary key value, if there is one:
+          if(primary_key_name == field_name && !Conversions::value_is_empty(primary_key_value))
+          {
+            value = primary_key_value;
+          }
+          else
+          {
+            if(use_entered_data)
+              value = get_entered_field_data(layout_item);
+          }
+
+          /* //TODO: This would be too many small queries when adding one record.
+          //Check whether the value meets uniqueness constraints:
+          if(field->get_primary_key() || field->get_unique_key())
+          {
+            if(!get_field_value_is_unique(m_table_name, layout_item, value))
+            {
+              //Ignore this field value. TODO: Warn the user about it.
+            } 
+          }
+          */
+
+          const Glib::ustring strFieldValue = field->sql(value);
+          if(!strFieldValue.empty())
+          {
+            if(!strNames.empty())
+            {
+              strNames += ", ";
+              strValues += ", ";
+            }
+  
+            strNames += "\"" + field_name + "\"";
+            strValues += strFieldValue;
+  
+            map_added[field_name] = true;
+          }
+        }
+      }
+    }
+  }
+
+  //Put it all together to create the record with these field values:
+  Glib::RefPtr<Gnome::Gda::DataModel> result;
+  if(!strNames.empty() && !strValues.empty())
+  {
+    const Glib::ustring strQuery = "INSERT INTO \"" + m_table_name + "\" (" + strNames + ") VALUES (" + strValues + ")";
+    result = query_execute(strQuery, App_Glom::get_application());
+    if(result)
+    {
+      Gtk::TreeModel::iterator row; // TODO: remove this parameter.
+      set_primary_key_value(row, primary_key_value); //Needed by Box_Data_List::on_adddel_user_changed().
+
+      //Update any lookups, related fields, or calculations:
+      for(type_vecLayoutFields::const_iterator iter = fieldsToAdd.begin(); iter != fieldsToAdd.end(); ++iter)
+      {
+         sharedptr<const LayoutItem_Field> layout_item = *iter;
+         
+         //TODO_Performance: We just set this with set_entered_field_data() above. Maybe we could just remember it.
+         const Gnome::Gda::Value field_value = get_entered_field_data(layout_item);
+
+         LayoutFieldInRecord field_in_record(layout_item, m_table_name, fieldPrimaryKey, primary_key_value);
+
+         //Get-and-set values for lookup fields, if this field triggers those relationships:
+         do_lookups(field_in_record, row, field_value);
+
+         //Update related fields, if this field is used in the relationship:
+         refresh_related_fields(field_in_record, row, field_value);
+      }
+    }
+    else
+    {
+      std::cerr << "Box_Data::record_new(): INSERT returned null DataModel." << std::endl; 
+    }
+  }
+
+  return result; //Failed.
+}
+
+
+bool Base_DB_Table_Data::get_related_record_exists(const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& key_field, const Gnome::Gda::Value& key_value)
+{
+  Bakery::BusyCursor cursor(App_Glom::get_application());
+
+  bool result = false;
+
+  //Don't try doing a NULL=NULL or ""="" relationship:
+  if(Glom::Conversions::value_is_empty(key_value))
+    return false;
+
+  //TODO_Performance: It's very possible that this is slow.
+  //We don't care how many records there are, only whether there are more than zero.
+  const Glib::ustring to_field = relationship->get_to_field();
+  const Glib::ustring related_table = relationship->get_to_table();
+
+  //TODO_Performance: Is this the best way to just find out whether there is one record that meets this criteria?
+  const Glib::ustring query = "SELECT \"" + to_field + "\" FROM \"" + relationship->get_to_table() + "\" WHERE \"" + related_table + "\".\"" + to_field + "\" = " + key_field->sql(key_value);
+  Glib::RefPtr<Gnome::Gda::DataModel> records = query_execute(query, App_Glom::get_application());
+  if(!records)
+    handle_error();
+  else
+  {
+    //Field contents:
+    if(records->get_n_rows())
+      result = true;
+  }
+
+  return result;
+}
+
+bool Base_DB_Table_Data::add_related_record_for_field(const sharedptr<const LayoutItem_Field>& layout_item_parent, 
+  const sharedptr<const Relationship>& relationship, 
+  const sharedptr<const Field>& primary_key_field, 
+  const Gnome::Gda::Value& primary_key_value_provided,
+  Gnome::Gda::Value& primary_key_value_used)
+{
+  Gnome::Gda::Value primary_key_value = primary_key_value_provided;
+
+  const bool related_record_exists = get_related_record_exists(relationship, primary_key_field, primary_key_value);
+  if(related_record_exists)
+  {
+    //No problem, the SQL command below will update this value in the related table.
+    primary_key_value_used = primary_key_value; //Let the caller know what related record was created.
+    return true;
+  }
+    
+
+  //To store the entered data in the related field, we would first have to create a related record.
+  if(!relationship->get_auto_create())
+  {
+    //Warn the user:
+    //TODO: Make the field insensitive until it can receive data, so people never see this dialog.
+    const Glib::ustring message = _("Data may not be entered into this related field, because the related record does not yet exist, and the relationship does not allow automatic creation of new related records.");
+#ifdef GLOM_ENABLE_MAEMO
+    Hildon::Note dialog(Hildon::NOTE_TYPE_INFORMATION, *App_Glom::get_application(), message);
+#else
+    Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Related Record Does Not Exist")), true);
+    dialog.set_secondary_text(message);
+    dialog.set_transient_for(*App_Glom::get_application());
+#endif
+    dialog.run();
+
+    //Clear the field again, discarding the entered data.
+    set_entered_field_data(layout_item_parent, Gnome::Gda::Value());
+
+    return false;
+  }
+  else
+  {
+    const bool key_is_auto_increment = primary_key_field->get_auto_increment();
+
+    //If we would have to set an otherwise auto-increment key to add the record.
+    if( key_is_auto_increment && !Conversions::value_is_empty(primary_key_value) )
+    {
+      //Warn the user:
+      //TODO: Make the field insensitive until it can receive data, so people never see this dialog.
+      const Glib::ustring message = _("Data may not be entered into this related field, because the related record does not yet exist, and the key in the related record is auto-generated and therefore can not be created with the key value in this record.");
+
+#ifdef GLOM_ENABLE_MAEMO
+      Hildon::Note dialog(Hildon::NOTE_TYPE_INFORMATION, *App_Glom::get_application(), message);
+#else
+      Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Related Record Cannot Be Created")), true);
+      //TODO: This is a very complex error message:
+      dialog.set_secondary_text(message);
+      dialog.set_transient_for(*App_Glom::get_application());
+#endif
+      dialog.run();
+
+      //Clear the field again, discarding the entered data.
+      set_entered_field_data(layout_item_parent, Gnome::Gda::Value());
+
+      return false;
+    }
+    else
+    {
+      //TODO: Calculate values, and do lookups?
+
+      //Create the related record:
+      if(key_is_auto_increment)
+      {
+        primary_key_value = get_next_auto_increment_value(relationship->get_to_table(), primary_key_field->get_name());
+
+        //Generate the new key value;
+      }
+
+      const Glib::ustring strQuery = "INSERT INTO \"" + relationship->get_to_table() + "\" (\"" + primary_key_field->get_name() + "\") VALUES (" + primary_key_field->sql(primary_key_value) + ")";
+      const bool test = query_execute(strQuery, App_Glom::get_application());
+      if(!test)
+        return false;
+
+      if(key_is_auto_increment)
+      {
+        //Set the key in the parent table
+        sharedptr<LayoutItem_Field> item_from_key = sharedptr<LayoutItem_Field>::create();
+        item_from_key->set_name(relationship->get_from_field());
+
+        //Show the new from key in the parent table's layout:
+        set_entered_field_data(item_from_key, primary_key_value);
+
+        //Set it in the database too:
+        sharedptr<Field> field_from_key = get_fields_for_table_one_field(relationship->get_from_table(), relationship->get_from_field()); //TODO_Performance.
+        if(!field_from_key)
+        {
+          std::cerr << "Base_DB_Table_Data::add_related_record_for_field(): get_fields_for_table_one_field() failed." << std::endl;
+          return false;
+        }
+
+        sharedptr<Field> parent_primary_key_field = get_field_primary_key();
+        if(!parent_primary_key_field)
+        {
+          g_warning("Base_DB_Table_Data::add_related_record_for_field(): get_field_primary_key() failed. table = %s", get_table_name().c_str());
+          return false;
+        }
+        else
+        {
+          const Gnome::Gda::Value parent_primary_key_value = get_primary_key_value_selected();
+          if(parent_primary_key_value.is_null())
+          {
+            g_warning("Base_DB_Table_Data::add_related_record_for_field(): get_primary_key_value_selected() failed. table = %s", get_table_name().c_str());
+            return false;
+          }
+          else
+          {
+            const Glib::ustring strQuery = "UPDATE \"" + relationship->get_from_table() + "\" SET \"" + relationship->get_from_field() + "\" = " + primary_key_field->sql(primary_key_value) +
+              " WHERE \"" + relationship->get_from_table() + "\".\"" + parent_primary_key_field->get_name() + "\" = " + parent_primary_key_field->sql(parent_primary_key_value);
+            const bool test = query_execute(strQuery, App_Glom::get_application());
+            if(!test)
+              return false;
+          }
+        }
+      }
+        
+      primary_key_value_used = primary_key_value; //Let the caller know what related record was created.
+      return true;
+    }
+  }
+}
+
+void Base_DB_Table_Data::on_record_added(const Gnome::Gda::Value& /* primary_key_value */, const Gtk::TreeModel::iterator& /* row */)
+{
+   //Overridden by some derived classes.
+  
+   signal_record_changed().emit();
+}
+
+void Base_DB_Table_Data::on_record_deleted(const Gnome::Gda::Value& /* primary_key_value */)
+{
+  //Overridden by some derived classes.
+  
+  signal_record_changed().emit();
+}
+
+bool Base_DB_Table_Data::confirm_delete_record()
+{
+  //Ask the user for confirmation:
+  const Glib::ustring message = _("Are you sure that you would like to delete this record? The data in this record will then be permanently lost.");
+#ifdef GLOM_ENABLE_MAEMO
+  Hildon::Note dialog(Hildon::NOTE_TYPE_CONFIRMATION_BUTTON, *get_app_window(), message);
+#else
+  Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Delete record")), true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE);
+  dialog.set_secondary_text(message);
+  dialog.set_transient_for(*App_Glom::get_application());
+#endif
+  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+  dialog.add_button(Gtk::Stock::DELETE, Gtk::RESPONSE_OK);
+
+  const int response = dialog.run();
+  return (response == Gtk::RESPONSE_OK);
+}
+
+bool Base_DB_Table_Data::record_delete(const Gnome::Gda::Value& primary_key_value)
+{
+  sharedptr<Field> field_primary_key = get_field_primary_key();
+  if(field_primary_key && !Conversions::value_is_empty(primary_key_value))
+  {
+    return query_execute( "DELETE FROM \"" + m_table_name + "\" WHERE \"" + m_table_name + "\".\"" + field_primary_key->get_name() + "\" = " + field_primary_key->sql(primary_key_value), App_Glom::get_application());
+  }
+  else
+  {
+    return false; //no primary key
+  }
+}
+
+Base_DB_Table_Data::type_signal_record_changed Base_DB_Table_Data::signal_record_changed()
+{
+  return m_signal_record_changed;
+}
+
+} //namespace Glom
+
+

Added: trunk/glom/base_db_table_data.h
==============================================================================
--- (empty file)
+++ trunk/glom/base_db_table_data.h	Tue Apr 29 14:32:35 2008
@@ -0,0 +1,93 @@
+/* Glom
+ *
+ * Copyright (C) 2001-2004 Murray Cumming
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef BASE_DB_TABLE_DATA_H
+#define BASE_DB_TABLE_DATA_H
+
+#include "base_db_table.h"
+#include <glom/libglom/data_structure/field.h>
+#include <algorithm> //find_if used in various places.
+
+namespace Glom
+{
+
+/** A base class some database functionality 
+ * for use with a specific database table, showing data from the table.
+ */
+class Base_DB_Table_Data : public Base_DB_Table
+{
+public: 
+  Base_DB_Table_Data();
+  virtual ~Base_DB_Table_Data();
+
+  virtual bool refresh_data_from_database();
+    
+  /** Tell the parent widget that something has changed in one of the shown records,
+   * or a record was added or deleted.
+   * This is only emitted for widgets for which it would be useful.
+   *
+   * @param relationship_name, if any.
+   */
+  typedef sigc::signal<void> type_signal_record_changed;
+  type_signal_record_changed signal_record_changed();
+
+protected:
+
+  /** Create a new record with all the entered field values from the currently-active details/row.
+   */
+  Glib::RefPtr<Gnome::Gda::DataModel> record_new(bool use_entered_data = true, const Gnome::Gda::Value& primary_key_value = Gnome::Gda::Value()); 
+
+  Gnome::Gda::Value get_entered_field_data_field_only(const sharedptr<const Field>& field) const;
+  virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
+    
+  virtual sharedptr<Field> get_field_primary_key() const = 0;
+  virtual Gnome::Gda::Value get_primary_key_value_selected() const = 0;
+  virtual void set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value) = 0;
+  virtual Gnome::Gda::Value get_primary_key_value(const Gtk::TreeModel::iterator& row) const = 0;
+      
+  /** Ask the user if he really wants to delete the record.
+   */  
+  bool confirm_delete_record();
+    
+  /** Delete a record from the database table.
+   * @param primary_key_value A primary key to indentify the record to delete.
+   */
+  bool record_delete(const Gnome::Gda::Value& primary_key_value);
+    
+  bool add_related_record_for_field(const sharedptr<const LayoutItem_Field>& layout_item_parent, const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& primary_key_field, const Gnome::Gda::Value& primary_key_value_provided, Gnome::Gda::Value& primary_key_value_used);
+
+  virtual void on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row); //Overridden by derived classes.
+  virtual void on_record_deleted(const Gnome::Gda::Value& primary_key_value); //Overridden by derived classes.
+      
+  FoundSet m_found_set;
+
+  type_vecFields m_TableFields; //A cache, so we don't have to repeatedly get them from the Document.
+  type_vecLayoutFields m_FieldsShown; //And any extra keys needed by shown fields.
+
+  type_signal_record_changed m_signal_record_changed;
+    
+private:
+  bool get_related_record_exists(const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& key_field, const Gnome::Gda::Value& key_value);
+};
+
+} //namespace Glom
+
+#endif //BASE_DB_TABLE__DATAH

Modified: trunk/glom/box_db_table.cc
==============================================================================
--- trunk/glom/box_db_table.cc	(original)
+++ trunk/glom/box_db_table.cc	Tue Apr 29 14:32:35 2008
@@ -31,7 +31,7 @@
 }
 
 Box_DB_Table::Box_DB_Table(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade)
-: Box_DB(cobject, refGlade)
+: Box_WithButtons(cobject, refGlade)
 {
 }
 
@@ -39,182 +39,17 @@
 {
 }
 
-Glib::ustring Box_DB_Table::get_table_name()
+const Gtk::Window* Box_DB_Table::get_app_window() const
 {
-  return m_table_name;
+  Box_DB_Table* nonconst = const_cast<Box_DB_Table*>(this);
+  return nonconst->get_app_window();
 }
-
-bool Box_DB_Table::init_db_details(const Glib::ustring& table_name)
-{
-  m_table_name = table_name;
-
-  if(!ConnectionPool::get_instance()->get_ready_to_connect())
-    return false;
-
-  return fill_from_database();
-}
-
-bool Box_DB_Table::refresh_data_from_database()
-{
-  if(!ConnectionPool::get_instance()->get_ready_to_connect())
-    return false;
-
-  return fill_from_database();
-}
-
-Gnome::Gda::Value Box_DB_Table::get_entered_field_data_field_only(const sharedptr<const Field>& field) const
-{
-  sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-  layout_item->set_full_field_details(field); 
-
-  return get_entered_field_data(layout_item);
-}
-
-Gnome::Gda::Value Box_DB_Table::get_entered_field_data(const sharedptr<const LayoutItem_Field>& /* field */) const
+  
+Gtk::Window* Box_DB_Table::get_app_window()
 {
-  //Override this to use Field::set_data() too.
-
-  return Gnome::Gda::Value(); //null
-}
-
-unsigned long Box_DB_Table::get_last_auto_increment_value(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model, const Glib::ustring& /* field_name */)
-{
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window());
-#else
-  std::auto_ptr<ExceptionConnection> error;
-  sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window(), error);
-#endif
-
-  if(sharedconnection)
-  {
-    Glib::RefPtr<Gnome::Gda::Connection> connection = sharedconnection->get_gda_connection();
-    Glib::ustring id = connection->get_last_insert_id(data_model);
-
-    //Convert it to a numeric type:
-    std::stringstream stream;
-    stream << id;
-    unsigned long id_numeric = 0;
-    stream >> id_numeric;
-
-    return id_numeric;
-  }
-  else
-    return 0;
-}
-
-//static:
-/*
-Box_DB_Table::type_vecFields Box_DB_Table::get_fields_for_datamodel(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model)
-{
-  type_vecFields result;
-
-  int columns =  data_model->get_n_columns();
-  for(int i = 0; i < columns; ++i)
-  {
-    Glib::RefPtr<Gnome::Gda::Column> fieldinfo = data_model->describe_column(i);
-    sharedptr<Field> field(new Field());
-    field->set_field_info(fieldinfo); //TODO: Get glom-specific information from document?
-    result.push_back( field );
-  }
-
-  return result;
+  return dynamic_cast<Gtk::Window*>(get_toplevel());
 }
-*/
-
-Glib::ustring Box_DB_Table::postgres_get_field_definition_for_sql(const Glib::RefPtr<const Gnome::Gda::Column>& field_info)
-{
-  Glib::ustring strResult;
 
-  //Type
-  Glib::ustring strType = "unknowntype";
-
-  //Postgres has a special "serial" datatype. (MySQL uses a numeric type, and has an extra "AUTO_INCREMENT" command)
-  if(false) //disabled for now - see generate_next_auto_increment() //field_info->get_auto_increment())
-  {
-    strType = "serial";
-  }
-  else
-  {
-    ConnectionPool* pConnectionPool = ConnectionPool::get_instance();
-    if(pConnectionPool)
-    {
-      const FieldTypes* pFieldTypes = pConnectionPool->get_field_types();
-      if(pFieldTypes)
-      {
-        const GType fieldType = field_info->get_g_type();
-        strType = pFieldTypes->get_string_name_for_gdavaluetype(fieldType);
-      }
-    }
-  }
-
-  strResult += strType;
-
-   /*
-  //Optinal type details: (M, D), UNSIGNED
-  Field::enumTypeOptionals optionals = fieldType.get_TypeOptionals();
-  if(optionals != Field::TYPE_OPTIONALS_None)
-  {
-  Glib::ustring strOptionals;
-
-  char pchM[10] = {0};
-  sprintf(pchM, "%d", fieldType.get_MaxLength());
-    Glib::ustring strM(pchM);
-
-
-  if(optionals == Field::TYPE_OPTIONALS_M_D)
-  {
-    if( (fieldType.get_MaxLength() != 0) && ( fieldType.get_DecimalsCount() != 0) ) //0 here means use default, so don't specify.
-    {
-      char pchD[10] = {0};
-      sprintf(pchD, "%d", fieldType.get_DecimalsCount());
-      Glib::ustring strD(pchD);
-
-      strOptionals = "(" + strM + "," + strD + ")";
-    }
-
-  }
-  else if(optionals == Field::TYPE_OPTIONALS_M_S)
-  {
-    if( fieldType.get_MaxLength() != 0 ) //0 here means use default, so don't specify.
-      strOptionals = "(" + strM + ")";
-
-      if(!(fieldType.get_Signed()))
-        strOptionals += " UNSIGNED";
-  }
-  else if(optionals == Field::TYPE_OPTIONALS_M)
-  {
-      if( fieldType.get_MaxLength() != 0 ) //0 here means use default, so don't specify.
-      strOptionals = "(" + strM + ")";
-  }
-
-    if(strOptionals.size())
-      strResult += (" " + strOptionals);
-  }
-  */
-
-  //Unique:
-  if(field_info->get_unique_key())
-    strResult += " UNIQUE";
-
-  /* Posgres needs us to do this separately
-  //Not Null:
-  if(!(field_info->get_allow_null()))
-    strResult += " NOT NULL";
-  */
-
-  //Default:
-  Gnome::Gda::Value valueDefault = field_info->get_default_value();
-  const Glib::ustring& strDefault =  valueDefault.to_string();
-  if(!strDefault.empty() && strDefault != "NULL")
-    strResult += " DEFAULT " + strDefault; //TODO: Quote/Escape it if necessary.
-
-  //Primary Key:
-  if(field_info->get_primary_key())
-    strResult += " PRIMARY KEY";
-
-  return strResult;
-}
 
 
 } //namespace Glom

Modified: trunk/glom/box_db_table.h
==============================================================================
--- trunk/glom/box_db_table.h	(original)
+++ trunk/glom/box_db_table.h	Tue Apr 29 14:32:35 2008
@@ -22,44 +22,28 @@
 #ifndef BOX_DB_TABLE_H
 #define BOX_DB_TABLE_H
 
-#include "box_db.h"
+#include <glom/box_withbuttons.h>
+#include <glom/base_db_table.h>
 #include <glom/libglom/data_structure/field.h>
+#include <libglademm.h>
 #include <algorithm> //find_if used in various places.
 
 namespace Glom
 {
 
-class Box_DB_Table : public Box_DB
+/** A Box that has access to a database table's structure.
+ */
+class Box_DB_Table
+: public Box_WithButtons,
+  public Base_DB_Table
 {
 public: 
   Box_DB_Table();
   Box_DB_Table(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
   virtual ~Box_DB_Table();
-
-  virtual bool init_db_details(const Glib::ustring& table_name);
-  virtual bool refresh_data_from_database();
-
-  virtual Glib::ustring get_table_name();
-
-  //TODO: Put this somewhere more sensible:
-  typedef std::map<GType, Glib::ustring> type_map_valuetypes;
-
-protected:
-
-  //virtual Glib::RefPtr<Gnome::Gda::DataModel> record_new(Gnome::Gda::Value primary_key_value);
-
-  Gnome::Gda::Value get_entered_field_data_field_only(const sharedptr<const Field>& field) const;
-  virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
-
-  //static sharedptr<Field> get_field_primary_key(const type_vecFields& fields);
-
-  unsigned long get_last_auto_increment_value(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model, const Glib::ustring& field_name);
-
-
-  //static type_vecFields get_fields_for_datamodel(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model); 
-  static Glib::ustring postgres_get_field_definition_for_sql(const Glib::RefPtr<const Gnome::Gda::Column>& field_info);
-
-  Glib::ustring m_table_name;
+    
+  Gtk::Window* get_app_window();
+  const Gtk::Window* get_app_window() const;
 };
 
 } //namespace Glom

Modified: trunk/glom/box_reports.cc
==============================================================================
--- trunk/glom/box_reports.cc	(original)
+++ trunk/glom/box_reports.cc	Tue Apr 29 14:32:35 2008
@@ -69,7 +69,7 @@
 {
   Bakery::BusyCursor busy_cursor(get_app_window());
 
-  bool result = Box_DB::fill_from_database();
+  bool result = Box_DB_Table::fill_from_database();
 
   //Enable/Disable extra widgets:
   bool developer_mode = (get_userlevel() == AppState::USERLEVEL_DEVELOPER);
@@ -117,8 +117,6 @@
 
   //TODO:
 
-  fill_end();
-
   m_AddDel.set_allow_add(developer_mode);
   m_AddDel.set_allow_delete(developer_mode);
 

Copied: trunk/glom/box_withbuttons.cc (from r1588, /trunk/glom/box_db.cc)
==============================================================================
--- /trunk/glom/box_db.cc	(original)
+++ trunk/glom/box_withbuttons.cc	Tue Apr 29 14:32:35 2008
@@ -18,7 +18,7 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#include "box_db.h"
+#include "box_withbuttons.h"
 #include "application.h" //App_Glom.
 #include <glom/libglom/appstate.h>
 //#include <libgnomeui/gnome-app-helper.h>
@@ -28,7 +28,7 @@
 namespace Glom
 {
 
-Box_DB::Box_DB()
+Box_WithButtons::Box_WithButtons()
 : m_Box_Buttons(false, Utils::DEFAULT_SPACING_SMALL),
   m_Button_Cancel(Gtk::Stock::CANCEL)
 {
@@ -38,10 +38,10 @@
   set_spacing(Utils::DEFAULT_SPACING_SMALL);
 
   //Connect signals:
-  m_Button_Cancel.signal_clicked().connect(sigc::mem_fun(*this, &Box_DB::on_Button_Cancel));
+  m_Button_Cancel.signal_clicked().connect(sigc::mem_fun(*this, &Box_WithButtons::on_Button_Cancel));
 }
 
-Box_DB::Box_DB(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& /* refGlade */)
+Box_WithButtons::Box_WithButtons(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& /* refGlade */)
 : Gtk::VBox(cobject),
   m_Box_Buttons(false, Utils::DEFAULT_SPACING_SMALL),
   m_Button_Cancel(Gtk::Stock::CANCEL)
@@ -52,33 +52,33 @@
   set_spacing(Utils::DEFAULT_SPACING_SMALL);
 
   //Connect signals:
-  m_Button_Cancel.signal_clicked().connect(sigc::mem_fun(*this, &Box_DB::on_Button_Cancel));
+  m_Button_Cancel.signal_clicked().connect(sigc::mem_fun(*this, &Box_WithButtons::on_Button_Cancel));
 }
 
-Box_DB::Box_DB(BaseObjectType* cobject)
+Box_WithButtons::Box_WithButtons(BaseObjectType* cobject)
 : Gtk::VBox(cobject),
   m_Box_Buttons(false, Utils::DEFAULT_SPACING_SMALL),
   m_Button_Cancel(Gtk::Stock::CANCEL)
 {
 }
 
-Box_DB::~Box_DB()
+Box_WithButtons::~Box_WithButtons()
 {
 }
 
-void Box_DB::on_Button_Cancel()
+void Box_WithButtons::on_Button_Cancel()
 {
   //Tell the parent dialog that the user has clicked [Cancel]:
   signal_cancelled.emit();
 }
 
-const Gtk::Window* Box_DB::get_app_window() const
+const Gtk::Window* Box_WithButtons::get_app_window() const
 {
-  Box_DB* nonconst = const_cast<Box_DB*>(this);
+  Box_WithButtons* nonconst = const_cast<Box_WithButtons*>(this);
   return nonconst->get_app_window();
 }
   
-Gtk::Window* Box_DB::get_app_window()
+Gtk::Window* Box_WithButtons::get_app_window()
 {
   return dynamic_cast<Gtk::Window*>(get_toplevel());
 /*
@@ -105,18 +105,18 @@
 }
 
 /*
-void Box_DB::show_hint()
+void Box_WithButtons::show_hint()
 {
   hint_set(m_strHint);
 }
 */
 
-void Box_DB::set_button_cancel(Gtk::Button& button)
+void Box_WithButtons::set_button_cancel(Gtk::Button& button)
 {
-  button.signal_clicked().connect(sigc::mem_fun(*this, &Box_DB::on_Button_Cancel));
+  button.signal_clicked().connect(sigc::mem_fun(*this, &Box_WithButtons::on_Button_Cancel));
 }
 
-Gtk::Widget* Box_DB::get_default_button()
+Gtk::Widget* Box_WithButtons::get_default_button()
 {
   return 0; //Override this if the box has a default button.
 }

Copied: trunk/glom/box_withbuttons.h (from r1588, /trunk/glom/box_db.h)
==============================================================================
--- /trunk/glom/box_db.h	(original)
+++ trunk/glom/box_withbuttons.h	Tue Apr 29 14:32:35 2008
@@ -18,10 +18,10 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef BOX_DB_H
-#define BOX_DB_H
+#ifndef GLOM_BOX_WITHBUTTONS_H
+#define GLOM_BOX_WITHBUTTONS_H
 
-#include "gtkmm.h"
+#include <gtkmm.h>
 #include "utility_widgets/adddel/adddel_withbuttons.h"
 
 #include <glom/libglom/document/document_glom.h>
@@ -34,18 +34,21 @@
 namespace Glom
 {
 
-class Box_DB :
-  public Gtk::VBox,
-  public Base_DB
+/** A Gtk::VBox base widget class, 
+ * with some extra signals to allow derived classes to be used generically in 
+ * Dialog_Glom, allowing the dialog to respond to buttons in the box.
+ */
+class Box_WithButtons :
+  public Gtk::VBox
 {
 public: 
-  Box_DB();
-  Box_DB(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
+  Box_WithButtons();
+  Box_WithButtons(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
 
   ///For use with libglademm's get_widget_derived():
-  Box_DB(BaseObjectType* cobject);
+  Box_WithButtons(BaseObjectType* cobject);
 
-  virtual ~Box_DB();
+  virtual ~Box_WithButtons();
 
   Gtk::Window* get_app_window();
   const Gtk::Window* get_app_window() const;
@@ -77,4 +80,4 @@
 
 } //namespace Glom
 
-#endif //BOX_DB_H
+#endif //GLOM_BOX_WITHBUTTONS_H

Modified: trunk/glom/dialog_connection.cc
==============================================================================
--- trunk/glom/dialog_connection.cc	(original)
+++ trunk/glom/dialog_connection.cc	Tue Apr 29 14:32:35 2008
@@ -19,7 +19,7 @@
  */
 
 #include "dialog_connection.h"
-#include "box_db.h" //For Box_DB::connect_to_server().
+#include "box_withbuttons.h" //For Box_WithButtons::connect_to_server().
 #include <glibmm/i18n.h>
 
 namespace Glom
@@ -108,12 +108,12 @@
       //}
     }
 
-    connection_pool->set_ready_to_connect(); //Box_DB::connect_to_server() will now attempt the connection-> Shared instances of m_Connection will also be usable.
+    connection_pool->set_ready_to_connect(); //Box_WithButtons::connect_to_server() will now attempt the connection-> Shared instances of m_Connection will also be usable.
 
 #ifdef GLIBMM_EXCEPTIONS_ENABLED
-    result = Box_DB::connect_to_server(const_cast<Dialog_Connection*>(this));
+    result = Base_DB::connect_to_server(const_cast<Dialog_Connection*>(this));
 #else
-    result = Box_DB::connect_to_server(const_cast<Dialog_Connection*>(this), error);
+    result = Base_DB::connect_to_server(const_cast<Dialog_Connection*>(this), error);
 #endif
 
     if(document)

Modified: trunk/glom/dialog_database_preferences.cc
==============================================================================
--- trunk/glom/dialog_database_preferences.cc	(original)
+++ trunk/glom/dialog_database_preferences.cc	Tue Apr 29 14:32:35 2008
@@ -19,7 +19,7 @@
  */
  
 #include "dialog_database_preferences.h"
-#include "box_db.h" //For Box_DB::connect_to_server().
+#include "box_withbuttons.h" //For Box_WithButtons::connect_to_server().
 #include <glom/libglom/standard_table_prefs_fields.h>
 #include <glom/libglom/data_structure/glomconversions.h>
 #include <bakery/Utilities/BusyCursor.h>

Modified: trunk/glom/dialog_glom.cc
==============================================================================
--- trunk/glom/dialog_glom.cc	(original)
+++ trunk/glom/dialog_glom.cc	Tue Apr 29 14:32:35 2008
@@ -23,7 +23,7 @@
 namespace Glom
 {
 
-Dialog_Glom::Dialog_Glom(Box_DB* pBox)
+Dialog_Glom::Dialog_Glom(Box_WithButtons* pBox)
 {
   set_border_width(Utils::DEFAULT_SPACING_SMALL);
 

Modified: trunk/glom/dialog_glom.h
==============================================================================
--- trunk/glom/dialog_glom.h	(original)
+++ trunk/glom/dialog_glom.h	Tue Apr 29 14:32:35 2008
@@ -21,7 +21,7 @@
 #ifndef DIALOG_GLOM_H
 #define DIALOG_GLOM_H
 
-#include "box_db.h"
+#include "box_withbuttons.h"
 #include <gtkmm/dialog.h>
 #include <gtkmm/button.h>
 
@@ -32,7 +32,7 @@
   public Gtk::Dialog
 {
 public: 
-  Dialog_Glom(Box_DB* pBox);
+  Dialog_Glom(Box_WithButtons* pBox);
   virtual ~Dialog_Glom();
 
   //Signal handlers:
@@ -41,7 +41,7 @@
 protected:
 
   //Member widgets:
-  Box_DB* m_pBox;
+  Box_WithButtons* m_pBox;
 };
 
 } //namespace Glom

Modified: trunk/glom/dialog_new_self_hosted_connection.cc
==============================================================================
--- trunk/glom/dialog_new_self_hosted_connection.cc	(original)
+++ trunk/glom/dialog_new_self_hosted_connection.cc	Tue Apr 29 14:32:35 2008
@@ -19,7 +19,7 @@
  */
 
 #include "dialog_new_self_hosted_connection.h"
-#include "box_db.h" //For Box_DB::connect_to_server().
+#include "box_withbuttons.h" //For Box_WithButtons::connect_to_server().
 #include <glom/frame_glom.h> //For Frame_Glom::show_ok_dialog
 #include <glibmm/i18n.h>
 

Modified: trunk/glom/glom_postgres.h
==============================================================================
--- trunk/glom/glom_postgres.h	(original)
+++ trunk/glom/glom_postgres.h	Tue Apr 29 14:32:35 2008
@@ -21,7 +21,7 @@
 #ifndef DB_POSTGRES_H
 #define DB_POSTGRES_H
 
-#include "gtkmm.h"
+#include <gtkmm.h>
 
 #include <glom/base_db.h>
 #include <glom/libglom/document/document_glom.h>

Modified: trunk/glom/glom_privs.h
==============================================================================
--- trunk/glom/glom_privs.h	(original)
+++ trunk/glom/glom_privs.h	Tue Apr 29 14:32:35 2008
@@ -21,7 +21,7 @@
 #ifndef GLOM_PRIVS_H
 #define GLOM_PRIVS_H
 
-#include "gtkmm.h"
+#include <gtkmm.h>
 
 #include <glom/glom_postgres.h>
 

Modified: trunk/glom/layout_item_dialogs/box_formatting.h
==============================================================================
--- trunk/glom/layout_item_dialogs/box_formatting.h	(original)
+++ trunk/glom/layout_item_dialogs/box_formatting.h	Tue Apr 29 14:32:35 2008
@@ -24,7 +24,7 @@
 #include <gtkmm.h>
 #include "../utility_widgets/dialog_properties.h"
 #include <glom/libglom/document/document_glom.h>
-#include "../box_db.h"
+#include "../box_withbuttons.h"
 #include "../utility_widgets/comboentry_currency.h"
 #include "../combobox_relationship.h"
 #include "../combobox_fields.h"

Modified: trunk/glom/layout_item_dialogs/dialog_field_layout.h
==============================================================================
--- trunk/glom/layout_item_dialogs/dialog_field_layout.h	(original)
+++ trunk/glom/layout_item_dialogs/dialog_field_layout.h	Tue Apr 29 14:32:35 2008
@@ -24,7 +24,7 @@
 #include <gtkmm.h>
 #include "../utility_widgets/dialog_properties.h"
 #include <glom/libglom/document/document_glom.h>
-#include "../box_db.h"
+#include "../box_withbuttons.h"
 #include "../utility_widgets/combo_textglade.h"
 #include "../utility_widgets/comboentry_currency.h"
 #include "box_formatting.h"

Modified: trunk/glom/layout_item_dialogs/dialog_field_summary.h
==============================================================================
--- trunk/glom/layout_item_dialogs/dialog_field_summary.h	(original)
+++ trunk/glom/layout_item_dialogs/dialog_field_summary.h	Tue Apr 29 14:32:35 2008
@@ -25,7 +25,7 @@
 #include "../utility_widgets/dialog_properties.h"
 #include <glom/libglom/document/document_glom.h>
 #include <glom/libglom/data_structure/layout/report_parts/layoutitem_fieldsummary.h>
-#include "../box_db.h"
+#include "../box_withbuttons.h"
 #include "../utility_widgets/combo_textglade.h"
 #include "combo_summarytype.h"
 

Modified: trunk/glom/layout_item_dialogs/dialog_group_by.h
==============================================================================
--- trunk/glom/layout_item_dialogs/dialog_group_by.h	(original)
+++ trunk/glom/layout_item_dialogs/dialog_group_by.h	Tue Apr 29 14:32:35 2008
@@ -24,7 +24,7 @@
 #include <gtkmm.h>
 #include "../utility_widgets/dialog_properties.h"
 #include <glom/libglom/document/document_glom.h>
-#include "../box_db.h"
+#include "../box_withbuttons.h"
 #include "../utility_widgets/combo_textglade.h"
 #include "../utility_widgets/comboentry_currency.h"
 #include "dialog_groupby_secondaryfields.h"

Modified: trunk/glom/libglom/data_structure/field.cc
==============================================================================
--- trunk/glom/libglom/data_structure/field.cc	(original)
+++ trunk/glom/libglom/data_structure/field.cc	Tue Apr 29 14:32:35 2008
@@ -452,7 +452,7 @@
 
 Glib::ustring Field::get_sql_type() const
 {
-  if(false) //See generate_next_auto_increment() //m_field_info->get_auto_increment())
+  if(false) //See get_next_auto_increment_value() //m_field_info->get_auto_increment())
     return "serial"; //See Postgres manual: http://www.postgresql.org/docs/7.4/interactive/datatype.html#DATATYPE-SERIAL. It's actually an integer..
   else
   {

Modified: trunk/glom/libglom/test_connectionpool.cc
==============================================================================
--- trunk/glom/libglom/test_connectionpool.cc	(original)
+++ trunk/glom/libglom/test_connectionpool.cc	Tue Apr 29 14:32:35 2008
@@ -45,7 +45,7 @@
       connection_pool->set_port(5433);
       connection_pool->set_try_other_ports(false);
 
-      connection_pool->set_ready_to_connect(); //Box_DB::connect_to_server() will now attempt the connection-> Shared instances of m_Connection will also be usable.
+      connection_pool->set_ready_to_connect(); //Box_WithButtons::connect_to_server() will now attempt the connection-> Shared instances of m_Connection will also be usable.
     }
 
     //Connect:

Modified: trunk/glom/mode_data/Makefile.am
==============================================================================
--- trunk/glom/mode_data/Makefile.am	(original)
+++ trunk/glom/mode_data/Makefile.am	Tue Apr 29 14:32:35 2008
@@ -12,6 +12,7 @@
                          box_data_list_related.cc box_data_list_related.h \
                          box_data_list.cc box_data_list.h \
                          box_data_calendar_related.cc box_data_calendar_related.h \
+                         box_data_manyrecords.cc box_data_manyrecords.h \
                          box_data_portal.cc box_data_portal.h \
                          notebook_data.cc notebook_data.h \
                          dialog_layout.cc dialog_layout.h \

Modified: trunk/glom/mode_data/box_data.cc
==============================================================================
--- trunk/glom/mode_data/box_data.cc	(original)
+++ trunk/glom/mode_data/box_data.cc	Tue Apr 29 14:32:35 2008
@@ -68,14 +68,14 @@
 
   create_layout(); //So that fill_from_database() can succeed.
 
-  return Box_DB_Table::init_db_details(m_table_name); //Calls fill_from_database().
+  return Base_DB_Table_Data::init_db_details(m_table_name); //Calls fill_from_database().
 }
 
 bool Box_Data::refresh_data_from_database_with_where_clause(const FoundSet& found_set)
 {
   m_found_set = found_set;
 
-  return Box_DB_Table::refresh_data_from_database(); //Calls fill_from_database().
+  return Base_DB_Table_Data::refresh_data_from_database(); //Calls fill_from_database().
 }
 
 FoundSet Box_Data::get_found_set() const
@@ -148,193 +148,6 @@
     signal_find_criteria.emit(where_clause);
 }
 
-Glib::RefPtr<Gnome::Gda::DataModel> Box_Data::record_new(bool use_entered_data, const Gnome::Gda::Value& primary_key_value)
-{
-  return record_new(use_entered_data, primary_key_value, Gtk::TreeModel::iterator());
-}
-
-void Box_Data::set_primary_key_value(const Gtk::TreeModel::iterator& /* row */, const Gnome::Gda::Value& /* value */)
-{
-  //Box_Data_List overrides this.
-}
-
-Glib::RefPtr<Gnome::Gda::DataModel> Box_Data::record_new(bool use_entered_data, const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row)
-{  
-  sharedptr<const Field> fieldPrimaryKey = get_field_primary_key();
-
-  const Glib::ustring primary_key_name = fieldPrimaryKey->get_name();
-
-  type_vecLayoutFields fieldsToAdd = m_FieldsShown;
-
-  //Add values for all fields, not just the shown ones:
-  //For instance, we must always add the primary key, and fields with default/calculated/lookup values:
-  for(type_vecFields::const_iterator iter = m_TableFields.begin(); iter != m_TableFields.end(); ++iter)
-  {
-    //TODO: Search for the non-related field with the name, not just the field with the name:
-    type_vecLayoutFields::const_iterator iterFind = std::find_if(fieldsToAdd.begin(), fieldsToAdd.end(), predicate_FieldHasName<LayoutItem_Field>((*iter)->get_name()));
-    if(iterFind == fieldsToAdd.end())
-    {
-      sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-      layout_item->set_full_field_details(*iter);
-
-      fieldsToAdd.push_back(layout_item);
-    }
-  }
-
-  Document_Glom* document = get_document();
-
-  //Calculate any necessary field values and enter them:
-  for(type_vecLayoutFields::const_iterator iter = fieldsToAdd.begin(); iter != fieldsToAdd.end(); ++iter)
-  {
-    sharedptr<LayoutItem_Field> layout_item = *iter;
-
-    //If the user did not enter something in this field:
-    Gnome::Gda::Value value = get_entered_field_data(layout_item);
-
-    if(Conversions::value_is_empty(value)) //This deals with empty strings too.
-    {
-      const sharedptr<const Field>& field = layout_item->get_full_field_details();
-      if(field)
-      {
-        //If the default value should be calculated, then calculate it:
-        if(field->get_has_calculation())
-        {
-          const Glib::ustring calculation = field->get_calculation();
-          const type_map_fields field_values = get_record_field_values_for_calculation(m_table_name, fieldPrimaryKey, primary_key_value);
-
-          //We need the connection when we run the script, so that the script may use it.
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-          // TODO: Is this function supposed to throw an exception?
-          sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window());
-#else
-          std::auto_ptr<ExceptionConnection> error;
-          sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window(), error);
-          if(error.get() == NULL)
-          {
-            // Don't evaluate function on error
-#endif // GLIBMM_EXCEPTIONS_ENABLED
-
-            const Gnome::Gda::Value value = glom_evaluate_python_function_implementation(field->get_glom_type(), calculation, field_values, document, m_table_name, sharedconnection->get_gda_connection());
-            set_entered_field_data(layout_item, value);
-#ifndef GLIBMM_EXCEPTIONS_ENABLED
-          }
-#endif // !GLIBMM_EXCEPTIONS_ENABLED
-        }
-
-        //Use default values (These are also specified in postgres as part of the field definition,
-        //so we could theoretically just not specify it here.)
-        //TODO_Performance: Add has_default_value()?
-        if(Conversions::value_is_empty(value))
-        {
-          const Gnome::Gda::Value default_value = field->get_default_value();
-          if(!Conversions::value_is_empty(default_value))
-          {
-            set_entered_field_data(layout_item, default_value);
-          }
-        }
-      }
-    }
-  }
-
-  //Get all entered field name/value pairs:
-  Glib::ustring strNames;
-  Glib::ustring strValues;
-
-  //Avoid specifying the same field twice:
-  typedef std::map<Glib::ustring, bool> type_map_added;
-  type_map_added map_added;
-
-  for(type_vecLayoutFields::const_iterator iter = fieldsToAdd.begin(); iter != fieldsToAdd.end(); ++iter)
-  {
-    sharedptr<LayoutItem_Field> layout_item = *iter;
-    const Glib::ustring field_name = layout_item->get_name();
-    if(!layout_item->get_has_relationship_name()) //TODO: Allow people to add a related record also by entering new data in a related field of the related record.
-    {
-      type_map_added::const_iterator iterFind = map_added.find(field_name);
-      if(iterFind == map_added.end()) //If it was not added already
-      {
-        Gnome::Gda::Value value;
-
-        const sharedptr<const Field>& field = layout_item->get_full_field_details();
-        if(field)
-        {
-          //Use the specified (generated) primary key value, if there is one:
-          if(primary_key_name == field_name && !Conversions::value_is_empty(primary_key_value))
-          {
-            value = primary_key_value;
-          }
-          else
-          {
-            if(use_entered_data)
-              value = get_entered_field_data(layout_item);
-          }
-
-          /* //TODO: This would be too many small queries when adding one record.
-          //Check whether the value meets uniqueness constraints:
-          if(field->get_primary_key() || field->get_unique_key())
-          {
-            if(!get_field_value_is_unique(m_table_name, layout_item, value))
-            {
-              //Ignore this field value. TODO: Warn the user about it.
-            } 
-          }
-          */
-
-          const Glib::ustring strFieldValue = field->sql(value);
-          if(!strFieldValue.empty())
-          {
-            if(!strNames.empty())
-            {
-              strNames += ", ";
-              strValues += ", ";
-            }
-  
-            strNames += "\"" + field_name + "\"";
-            strValues += strFieldValue;
-  
-            map_added[field_name] = true;
-          }
-        }
-      }
-    }
-  }
-
-  //Put it all together to create the record with these field values:
-  Glib::RefPtr<Gnome::Gda::DataModel> result;
-  if(!strNames.empty() && !strValues.empty())
-  {
-    const Glib::ustring strQuery = "INSERT INTO \"" + m_table_name + "\" (" + strNames + ") VALUES (" + strValues + ")";
-    result = query_execute(strQuery, get_app_window());
-    if(result)
-    {
-      set_primary_key_value(row, primary_key_value); //Needed by Box_Data_List::on_adddel_user_changed().
-
-      //Update any lookups, related fields, or calculations:
-      for(type_vecLayoutFields::const_iterator iter = fieldsToAdd.begin(); iter != fieldsToAdd.end(); ++iter)
-      {
-         sharedptr<const LayoutItem_Field> layout_item = *iter;
-         
-         //TODO_Performance: We just set this with set_entered_field_data() above. Maybe we could just remember it.
-         const Gnome::Gda::Value field_value = get_entered_field_data(layout_item);
-
-         LayoutFieldInRecord field_in_record(layout_item, m_table_name, fieldPrimaryKey, primary_key_value);
-
-         //Get-and-set values for lookup fields, if this field triggers those relationships:
-         do_lookups(field_in_record, row, field_value);
-
-         //Update related fields, if this field is used in the relationship:
-         refresh_related_fields(field_in_record, row, field_value);
-      }
-    }
-    else
-    {
-      std::cerr << "Box_Data::record_new(): INSERT returned null DataModel." << std::endl; 
-    }
-  }
-
-  return result; //Failed.
-}
-
 void Box_Data::set_unstored_data(bool bVal)
 {
   m_bUnstoredData = bVal;
@@ -357,7 +170,7 @@
 {
   set_unstored_data(false);
 
-  return Box_DB_Table::fill_from_database();
+  return Base_DB_Table_Data::fill_from_database();
 }
 
 bool Box_Data::confirm_discard_unstored_data() const
@@ -432,39 +245,6 @@
     return type_vecLayoutFields();
 }
 
-Gnome::Gda::Value Box_Data::generate_next_auto_increment(const Glib::ustring& table_name, const Glib::ustring field_name)
-{
-  //Get it from the database system table.
-  //The developer can change the next value in the Database Preferences
-  return get_next_auto_increment_value(table_name, field_name);
-
-  /*
-  //This is a workaround for postgres problems. Ideally, we need to use the postgres serial type and find out how to get the generated value after we add a row.
-
-  double result = 0;
-  Glib::RefPtr<Gnome::Gda::DataModel> data_model = query_execute("SELECT max(" + field_name + ") FROM \"" + table_name + "\"");
-  if(!data_model && data_model->get_n_rows())
-    handle_error();
-  else
-  {
-    //The result should be 1 row with 1 column
-    Gnome::Gda::Value value = data_model->get_value_at(0, 0);
-
-    //It's a Gnome::Gda::Value::TYPE_NUMERIC, but the GdaNumeric struct is not easy to handle, so let's hack around it:
-    //if(value.is_number())
-    //  result = value.get_integer();
-    //else
-    result = get_double_for_gda_value_numeric(value);
-
-    ++result; 
-  }
-
-  //Get a string representation of the number, so we can put it back in a NUMERIC Gda::Value:
-
-  return Conversions::parse_value(result);
-  */
-}
-
 /** Get the shown fields that are in related tables, via a relationship using @a field_name changes.
  */
 Box_Data::type_vecLayoutFields Box_Data::get_related_fields(const sharedptr<const LayoutItem_Field>& field) const
@@ -634,205 +414,6 @@
   }
 }
 
-bool Box_Data::record_delete(const Gnome::Gda::Value& primary_key_value)
-{
-
-  sharedptr<Field> field_primary_key = get_field_primary_key();
-  if(field_primary_key && !Conversions::value_is_empty(primary_key_value))
-  {
-    return query_execute( "DELETE FROM \"" + m_table_name + "\" WHERE \"" + m_table_name + "\".\"" + field_primary_key->get_name() + "\" = " + field_primary_key->sql(primary_key_value), get_app_window());
-  }
-  else
-  {
-    return false; //no primary key
-  }
-}
-
-/*
-bool Box_Data::get_field(const Glib::ustring& name, Field& field) const
-{
-g_warning("debug: Box_Data::get_field()");
-  type_vecFields::const_iterator iterFind = std::find_if( m_TableFields.begin(), m_TableFields.end(), predicate_FieldHasName<LayoutItem_Field>(name) );
-  if(iterFind != m_FieldsShown.end()) //If it was found:
-  {
-    field = iterFind->m_field;
-    return true;
-  }
-  else
-  {
-    return false; //not found.
-  }
-}
-*/
-
-bool Box_Data::get_related_record_exists(const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& key_field, const Gnome::Gda::Value& key_value)
-{
-  Bakery::BusyCursor cursor(get_app_window());
-
-  bool result = false;
-
-  //Don't try doing a NULL=NULL or ""="" relationship:
-  if(Glom::Conversions::value_is_empty(key_value))
-    return false;
-
-  //TODO_Performance: It's very possible that this is slow.
-  //We don't care how many records there are, only whether there are more than zero.
-  const Glib::ustring to_field = relationship->get_to_field();
-  const Glib::ustring related_table = relationship->get_to_table();
-
-  //TODO_Performance: Is this the best way to just find out whether there is one record that meets this criteria?
-  const Glib::ustring query = "SELECT \"" + to_field + "\" FROM \"" + relationship->get_to_table() + "\" WHERE \"" + related_table + "\".\"" + to_field + "\" = " + key_field->sql(key_value);
-  Glib::RefPtr<Gnome::Gda::DataModel> records = query_execute(query, get_app_window());
-  if(!records)
-    handle_error();
-  else
-  {
-    //Field contents:
-    if(records->get_n_rows())
-      result = true;
-  }
-
-  return result;
-}
-
-bool Box_Data::add_related_record_for_field(const sharedptr<const LayoutItem_Field>& layout_item_parent, 
-  const sharedptr<const Relationship>& relationship, 
-  const sharedptr<const Field>& primary_key_field, 
-  const Gnome::Gda::Value& primary_key_value_provided,
-  Gnome::Gda::Value& primary_key_value_used)
-{
-  Gnome::Gda::Value primary_key_value = primary_key_value_provided;
-
-  const bool related_record_exists = get_related_record_exists(relationship, primary_key_field, primary_key_value);
-  if(related_record_exists)
-  {
-    //No problem, the SQL command below will update this value in the related table.
-    primary_key_value_used = primary_key_value; //Let the caller know what related record was created.
-    return true;
-  }
-    
-
-  //TODO: Remove this useless {} scope. It is only still here to avoid whitespace changes in a patch diff.
-  {
-    //To store the entered data in the related field, we would first have to create a related record.
-    if(!relationship->get_auto_create())
-    {
-      //Warn the user:
-      //TODO: Make the field insensitive until it can receive data, so people never see this dialog.
-      const Glib::ustring message = _("Data may not be entered into this related field, because the related record does not yet exist, and the relationship does not allow automatic creation of new related records.");
-#ifdef GLOM_ENABLE_MAEMO
-      Hildon::Note dialog(Hildon::NOTE_TYPE_INFORMATION, *get_app_window(), message);
-#else
-      Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Related Record Does Not Exist")), true);
-      dialog.set_secondary_text(message);
-      dialog.set_transient_for(*get_app_window());
-#endif
-      dialog.run();
-
-      //Clear the field again, discarding the entered data.
-      set_entered_field_data(layout_item_parent, Gnome::Gda::Value());
-
-      return false;
-    }
-    else
-    {
-      const bool key_is_auto_increment = primary_key_field->get_auto_increment();
-
-      //If we would have to set an otherwise auto-increment key to add the record.
-      if( key_is_auto_increment && !Conversions::value_is_empty(primary_key_value) )
-      {
-        //Warn the user:
-        //TODO: Make the field insensitive until it can receive data, so people never see this dialog.
-        const Glib::ustring message = _("Data may not be entered into this related field, because the related record does not yet exist, and the key in the related record is auto-generated and therefore can not be created with the key value in this record.");
-
-#ifdef GLOM_ENABLE_MAEMO
-        Hildon::Note dialog(Hildon::NOTE_TYPE_INFORMATION, *get_app_window(), message);
-#else
-        Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Related Record Cannot Be Created")), true);
-        //TODO: This is a very complex error message:
-        dialog.set_secondary_text(message);
-        dialog.set_transient_for(*get_app_window());
-#endif
-        dialog.run();
-
-        //Clear the field again, discarding the entered data.
-        set_entered_field_data(layout_item_parent, Gnome::Gda::Value());
-
-        return false;
-      }
-      else
-      {
-        //TODO: Calculate values, and do lookups?
-
-        //Create the related record:
-        if(key_is_auto_increment)
-        {
-          primary_key_value = generate_next_auto_increment(relationship->get_to_table(), primary_key_field->get_name());
-
-          //Generate the new key value;
-        }
-
-        const Glib::ustring strQuery = "INSERT INTO \"" + relationship->get_to_table() + "\" (\"" + primary_key_field->get_name() + "\") VALUES (" + primary_key_field->sql(primary_key_value) + ")";
-        const bool test = query_execute(strQuery, get_app_window());
-        if(!test)
-          return false;
-
-        //TODO: Remove this useless {} scope. It is only still here to avoid whitespace changes in a patch diff.
-        {
-          if(key_is_auto_increment)
-          {
-            //Set the key in the parent table
-            sharedptr<LayoutItem_Field> item_from_key = sharedptr<LayoutItem_Field>::create();
-            item_from_key->set_name(relationship->get_from_field());
-
-            //Show the new from key in the parent table's layout:
-            set_entered_field_data(item_from_key, primary_key_value);
-
-            //Set it in the database too:
-            sharedptr<Field> field_from_key = get_fields_for_table_one_field(relationship->get_from_table(), relationship->get_from_field()); //TODO_Performance.
-            if(!field_from_key)
-            {
-              std::cerr << "Box_Data::add_related_record_for_field(): get_fields_for_table_one_field() failed." << std::endl;
-              return false;
-            }
-
-            //TODO: Remove this useless {} scope. It is only still here to avoid whitespace changes in a patch diff.
-            {
-              sharedptr<Field> parent_primary_key_field = get_field_primary_key();
-              if(!parent_primary_key_field)
-              {
-                g_warning("Box_Data::add_related_record_for_field(): get_field_primary_key() failed. table = %s", get_table_name().c_str());
-                return false;
-              }
-              else
-              {
-                const Gnome::Gda::Value parent_primary_key_value = get_primary_key_value_selected();
-                if(parent_primary_key_value.is_null())
-                {
-                  g_warning("Box_Data::add_related_record_for_field(): get_primary_key_value_selected() failed. table = %s", get_table_name().c_str());
-                  return false;
-                }
-                else
-                {
-                  const Glib::ustring strQuery = "UPDATE \"" + relationship->get_from_table() + "\" SET \"" + relationship->get_from_field() + "\" = " + primary_key_field->sql(primary_key_value) +
-                    " WHERE \"" + relationship->get_from_table() + "\".\"" + parent_primary_key_field->get_name() + "\" = " + parent_primary_key_field->sql(parent_primary_key_value);
-                  const bool test = query_execute(strQuery, get_app_window());
-                  if(!test)
-                    return false;
-                }
-              }
-            }
-          }
-
-           primary_key_value_used = primary_key_value; //Let the caller know what related record was created.
-           return true;
-        }
-      }
-    }
-
-  }
-}
-
 void Box_Data::print_layout()
 {
   const Glib::ustring message = "Sorry, this feature has not been implemented yet.";
@@ -851,24 +432,6 @@
   return m_layout_name;
 }
 
-bool Box_Data::confirm_delete_record()
-{
-  //Ask the user for confirmation:
-  const Glib::ustring message = _("Are you sure that you would like to delete this record? The data in this record will then be permanently lost.");
-#ifdef GLOM_ENABLE_MAEMO
-  Hildon::Note dialog(Hildon::NOTE_TYPE_CONFIRMATION_BUTTON, *get_app_window(), message);
-#else
-  Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Delete record")), true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE);
-  dialog.set_secondary_text(message);
-  dialog.set_transient_for(*get_app_window());
-#endif
-  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
-  dialog.add_button(Gtk::Stock::DELETE, Gtk::RESPONSE_OK);
-
-  const int response = dialog.run();
-  return (response == Gtk::RESPONSE_OK);
-}
-
 void Box_Data::execute_button_script(const sharedptr<const LayoutItem_Button>& layout_item, const Gnome::Gda::Value& primary_key_value)
 {
   const type_map_fields field_values = get_record_field_values_for_calculation(m_table_name, get_field_primary_key(), primary_key_value);

Modified: trunk/glom/mode_data/box_data.h
==============================================================================
--- trunk/glom/mode_data/box_data.h	(original)
+++ trunk/glom/mode_data/box_data.h	Tue Apr 29 14:32:35 2008
@@ -23,24 +23,30 @@
 
 #include "config.h" // GLOM_ENABLE_CLIENT_ONLY
 
-#include "../box_db_table.h"
+#include <glom/box_withbuttons.h>
+#include <glom/base_db_table_data.h>
 #include "dialog_layout.h"
 
 namespace Glom
 {
 
-/** Call init_db_details() to create the layout and fill it with data from the database.
+/** A Box for viewing and editing the data in a database table.
+ *
+ * Call init_db_details() to create the layout and fill it with data from the database.
  * Call refresh_data_from_database() to fill the existing layout with up-to-date data from the database.
  *
  * Derived classes should implement create_layout() to create/arrange the widgets for the groups, fields, portals, etc.
  * Derived classes should implement fill_from_database() to get the data from the database and fill the widgets created by create_layout().
  */
-class Box_Data : public Box_DB_Table
+class Box_Data
+: public Box_WithButtons,
+  public Base_DB_Table_Data
 {
 public: 
   Box_Data();
   virtual ~Box_Data();
 
+  //TODO: Put this in Base_DB_Table_Data?
   ///Create the layout for the database structure, and fill it with data from the database.
   virtual bool init_db_details(const FoundSet& found_set);
 
@@ -94,9 +100,6 @@
 
   virtual type_vecLayoutFields get_fields_to_show() const;
 
-  virtual bool get_related_record_exists(const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& key_field, const Gnome::Gda::Value& key_value);
-  virtual bool add_related_record_for_field(const sharedptr<const LayoutItem_Field>& layout_item_parent, const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& primary_key_field, const Gnome::Gda::Value& primary_key_value_provided, Gnome::Gda::Value& primary_key_value_used);
-
   type_vecLayoutFields get_table_fields_to_show(const Glib::ustring& table_name) const;
 
   /** Get the layout groups, with the Field information filled in.
@@ -109,23 +112,6 @@
   */
   type_vecLayoutFields get_related_fields(const sharedptr<const LayoutItem_Field>& field) const;
 
-  bool record_delete(const Gnome::Gda::Value& primary_key_value);
-
-  ///This allows record_new() to set the generated/entered primary key value, needed by Box_Data_List:
-  virtual void set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value);
-
-  ///New record with all entered field values.
-  Glib::RefPtr<Gnome::Gda::DataModel> record_new(bool use_entered_data = true, const Gnome::Gda::Value& primary_key_value = Gnome::Gda::Value()); 
-  Glib::RefPtr<Gnome::Gda::DataModel> record_new(bool use_entered_data, const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row);
-
-  Gnome::Gda::Value generate_next_auto_increment(const Glib::ustring& table_name, const Glib::ustring field_name);
-
-  virtual sharedptr<Field> get_field_primary_key() const = 0;
-  virtual Gnome::Gda::Value get_primary_key_value_selected() = 0;
-  //virtual bool get_field(const Glib::ustring& name, sharedptr<Field>& field) const;
-
-  bool confirm_delete_record();
-
   void execute_button_script(const sharedptr<const LayoutItem_Button>& layout_item, const Gnome::Gda::Value& primary_key_value);
 
   //Signal handlers:
@@ -147,11 +133,6 @@
   Dialog_Layout* m_pDialogLayout;
 #endif // !GLOM_ENABLE_CLIENT_ONLY
   Glib::ustring m_layout_name;
-
-  FoundSet m_found_set;
-
-  type_vecFields m_TableFields; //A cache, so we don't have to repeatedly get them from the Document.
-  type_vecLayoutFields m_FieldsShown; //And any extra keys needed by shown fields.
 };
 
 } //namespace Glom

Modified: trunk/glom/mode_data/box_data_calendar_related.cc
==============================================================================
--- trunk/glom/mode_data/box_data_calendar_related.cc	(original)
+++ trunk/glom/mode_data/box_data_calendar_related.cc	Tue Apr 29 14:32:35 2008
@@ -35,7 +35,6 @@
 {
   set_size_request(400, -1); //An arbitrary default.
 
-  remove(m_AddDel); //TODO: Don't even have this at all.
   m_Alignment.add(m_calendar);
   m_calendar.show();
   
@@ -71,7 +70,7 @@
   m_portal = glom_sharedptr_clone(portal);
 
   LayoutWidgetBase::m_table_name = m_portal->get_table_used(Glib::ustring() /* parent table_name, not used. */); 
-  Box_DB_Table::m_table_name = LayoutWidgetBase::m_table_name;
+ Base_DB_Table::m_table_name = LayoutWidgetBase::m_table_name;
 
   const Glib::ustring relationship_title = m_portal->get_title_used(Glib::ustring() /* parent title - not relevant */);
   
@@ -96,7 +95,7 @@
 
   FoundSet found_set;
   found_set.m_table_name = LayoutWidgetBase::m_table_name;
-  return Box_Data_List::init_db_details(found_set); //Calls create_layout() and fill_from_database().
+  return Box_Data::init_db_details(found_set); //Calls create_layout() and fill_from_database().
 }
 
 bool Box_Data_Calendar_Related::fill_from_database()
@@ -110,11 +109,9 @@
   {
     //No Foreign Key value, so just show the field names:
 
-    result = Box_DB_Table::fill_from_database();
+    result = Base_DB_Table_Data::fill_from_database();
 
     //create_layout();
-
-    fill_end();
   }
   else
   {
@@ -214,6 +211,8 @@
   
   m_map_values.clear(); 
 }
+
+//TODO: Make this generic in Box_Data_Portal:
 void Box_Data_Calendar_Related::on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row)
 {
   //primary_key_value is a new autogenerated or human-entered key for the row.
@@ -232,8 +231,6 @@
     //TODO: key_value = m_calendar.get_value(row, layout_item);
   }
 
-  Box_Data_List::on_record_added(key_value, row); //adds blank row.
-
   //Make sure that the new related record is related,
   //by setting the foreign key:
   //If it's not auto-generated.
@@ -271,12 +268,6 @@
   }
 }
 
-void Box_Data_Calendar_Related::on_record_deleted(const Gnome::Gda::Value& /* primary_key_value */)
-{
-  //Allow the parent record (Details view) to recalculate aggregations:
-  signal_record_changed().emit(m_portal->get_relationship_name());
-}
-
 Box_Data_Calendar_Related::type_vecLayoutFields Box_Data_Calendar_Related::get_fields_to_show() const
 {
   type_vecLayoutFields layout_fields = Box_Data_Portal::get_fields_to_show();
@@ -572,5 +563,22 @@
 }
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
+Gnome::Gda::Value Box_Data_Calendar_Related::get_primary_key_value(const Gtk::TreeModel::iterator& row) const
+{
+  return Gnome::Gda::Value(); //TODO: m_AddDel.get_value_key(row);
+}
+
+Gnome::Gda::Value Box_Data_Calendar_Related::get_primary_key_value_selected() const
+{
+  return Gnome::Gda::Value(); //TODO: m_AddDel.get_value_key_selected();
+}
+
+void Box_Data_Calendar_Related::set_primary_key_value(const Gtk::TreeModel::iterator& /* row */, const Gnome::Gda::Value& /* value */)
+{
+  //TODO
+}
+    
+
+
 
 } //namespace Glom

Modified: trunk/glom/mode_data/box_data_calendar_related.h
==============================================================================
--- trunk/glom/mode_data/box_data_calendar_related.h	(original)
+++ trunk/glom/mode_data/box_data_calendar_related.h	Tue Apr 29 14:32:35 2008
@@ -47,15 +47,23 @@
 protected:
   virtual bool fill_from_database(); //Override.
   virtual type_vecLayoutFields get_fields_to_show() const; //override
+    
+    
+  //Implementations of pure virtual methods from Base_DB_Table_Data:
+  virtual void set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value);
+    
 
-  virtual void on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row); //Override. Not a signal handler.
-  virtual void on_record_deleted(const Gnome::Gda::Value& primary_key_value); //override.
+  void on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row); //Override. Not a signal handler.
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
   virtual void on_dialog_layout_hide(); //override.
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
   virtual void enable_buttons(); //override
+    
+  //Implementations of pure virtual methods from Base_DB_Table_Data:
+  virtual Gnome::Gda::Value get_primary_key_value_selected() const;
+  virtual Gnome::Gda::Value get_primary_key_value(const Gtk::TreeModel::iterator& row) const;
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
   virtual Dialog_Layout* create_layout_dialog() const; // override.

Modified: trunk/glom/mode_data/box_data_details.cc
==============================================================================
--- trunk/glom/mode_data/box_data_details.cc	(original)
+++ trunk/glom/mode_data/box_data_details.cc	Tue Apr 29 14:32:35 2008
@@ -135,9 +135,9 @@
   remove_view(&m_FlowTable);
 }
 
-Gnome::Gda::Value Box_Data_Details::get_primary_key_value() const
+Gnome::Gda::Value Box_Data_Details::get_primary_key_value(const Gtk::TreeModel::iterator& /* row */) const
 {
-  return m_primary_key_value;
+  return get_primary_key_value_selected();
 }
 
 void Box_Data_Details::set_primary_key_value(const Gtk::TreeModel::iterator& /* row */, const Gnome::Gda::Value& value)
@@ -359,9 +359,6 @@
     //fill_related();
 
     set_unstored_data(false);
-
-    fill_end();
-
   }
 
   return bResult;
@@ -391,7 +388,7 @@
   if(m_field_primary_key && m_field_primary_key->get_auto_increment()) //If the primary key is an auto-increment:
   {
     //Just make a new record, and show it:
-    Gnome::Gda::Value primary_key_value = generate_next_auto_increment(m_table_name, m_field_primary_key->get_name()); //TODO: This should return a Gda::Value
+    Gnome::Gda::Value primary_key_value = get_next_auto_increment_value(m_table_name, m_field_primary_key->get_name()); //TODO: This should return a Gda::Value
 
     record_new(false /* use entered field data */, primary_key_value);
     refresh_data_from_database_with_primary_key(primary_key_value);
@@ -406,7 +403,7 @@
 
 void Box_Data_Details::on_button_del()
 {
-  if( Conversions::value_is_empty(get_primary_key_value()) )
+  if( Conversions::value_is_empty(get_primary_key_value_selected()) )
   {
     //Tell user that a primary key is needed to delete a record:
     Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("No primary key value.")), true);
@@ -469,7 +466,7 @@
   set_entered_field_data(field, value);
 }
 
-Gnome::Gda::Value Box_Data_Details::get_primary_key_value_selected()
+Gnome::Gda::Value Box_Data_Details::get_primary_key_value_selected() const
 {
   return m_primary_key_value;
 }
@@ -479,7 +476,7 @@
   m_FieldsCalculationInProgress.clear();
 
   //Check all fields in the parent table:
-  const Gnome::Gda::Value primary_key_value = get_primary_key_value();
+  const Gnome::Gda::Value primary_key_value = get_primary_key_value_selected();
   for(type_vecFields::iterator iter = m_TableFields.begin(); iter != m_TableFields.end(); ++iter)
   {
     const sharedptr<const Field> field = *iter;
@@ -625,7 +622,7 @@
 {
   if(layout_item)
   {
-    const Gnome::Gda::Value primary_key_value = get_primary_key_value();
+    const Gnome::Gda::Value primary_key_value = get_primary_key_value_selected();
     execute_button_script(layout_item, primary_key_value);
 
     //Refresh the view, in case the script changed any data:
@@ -653,7 +650,7 @@
 
   Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
 
-  Gnome::Gda::Value primary_key_value = get_primary_key_value();
+  Gnome::Gda::Value primary_key_value = get_primary_key_value_selected();
   if(!Conversions::value_is_empty(primary_key_value)) //If there is not a primary key value:
   {
     Glib::ustring table_name;
@@ -664,7 +661,7 @@
     {
       table_name = get_table_name();
       primary_key_field = m_field_primary_key;
-      primary_key_value = get_primary_key_value();
+      primary_key_value = get_primary_key_value_selected();
     }
     else
     {
@@ -800,7 +797,7 @@
       else
       {
         //Make a new record, and show it:
-        Gnome::Gda::Value primary_key_value = generate_next_auto_increment(m_table_name, m_field_primary_key->get_name());
+        Gnome::Gda::Value primary_key_value = get_next_auto_increment_value(m_table_name, m_field_primary_key->get_name());
 
         record_new(true /* use entered field data */, primary_key_value);
         refresh_data_from_database_with_primary_key(primary_key_value);
@@ -835,7 +832,7 @@
         set_unstored_data(true); //Cause a warning if this is never put into the database.
       }
     }
-  } //if(get_primary_key_value().size())
+  } //if(get_primary_key_value_selected().size())
 }
 
 void Box_Data_Details::on_userlevel_changed(AppState::userlevels user_level)

Modified: trunk/glom/mode_data/box_data_details.h
==============================================================================
--- trunk/glom/mode_data/box_data_details.h	(original)
+++ trunk/glom/mode_data/box_data_details.h	Tue Apr 29 14:32:35 2008
@@ -46,15 +46,7 @@
 
   virtual void print_layout();
 
-  virtual Gnome::Gda::Value get_primary_key_value() const; //Actual primary key value of this record.
-  virtual Gnome::Gda::Value get_primary_key_value_selected(); //Value in the primary key's cell.
-
-  virtual void set_primary_key_value(const Gtk::TreeModel::iterator& /* row */, const Gnome::Gda::Value& value);
-
-  virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
-  virtual void set_entered_field_data(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
-  virtual void set_entered_field_data(const Gtk::TreeModel::iterator& row, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
-
+ 
   //Signals:
   typedef sigc::signal<void> type_signal_void;
   type_signal_void signal_nav_first();
@@ -62,7 +54,7 @@
   type_signal_void signal_nav_next();
   type_signal_void signal_nav_last();
 
-  typedef sigc::signal<void, const Gnome::Gda::Value&> type_signal_record_deleted; //arg is PrimaryKey.   //TODO: pass by const ref?
+  typedef sigc::signal<void, const Gnome::Gda::Value&> type_signal_record_deleted; //arg is PrimaryKey.
   type_signal_record_deleted signal_record_deleted();
 
    /** For instance,
@@ -72,6 +64,18 @@
   type_signal_requested_related_details signal_requested_related_details();
 
 protected:
+
+
+  //Implementations of pure virtual methods from Base_DB_Table_Data:
+  virtual Gnome::Gda::Value get_primary_key_value_selected() const; //Value in the primary key's cell.
+  virtual void set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value);
+  virtual Gnome::Gda::Value get_primary_key_value(const Gtk::TreeModel::iterator& row) const; //Actual primary key value of this record.
+    
+  virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
+  virtual void set_entered_field_data(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
+  virtual void set_entered_field_data(const Gtk::TreeModel::iterator& row, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
+
+
   virtual bool fill_from_database(); //override.
   virtual void create_layout();
   //virtual void fill_related();

Modified: trunk/glom/mode_data/box_data_list.cc
==============================================================================
--- trunk/glom/mode_data/box_data_list.cc	(original)
+++ trunk/glom/mode_data/box_data_list.cc	Tue Apr 29 14:32:35 2008
@@ -43,17 +43,14 @@
 
   pack_start(m_AddDel);
   add_view(&m_AddDel); //Give it access to the document.
-  m_AddDel.set_auto_add(false); //We want to add the row ourselves when the user clicks the Add button, because the default behaviour there is not suitable.
   m_AddDel.set_rules_hint(); //Use alternating row colors when the theme does that.
 
   //Connect signals:
-  m_AddDel.signal_user_requested_add().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_requested_add)); //Only emitted when m_AddDel.set_auto_add(false) is used.
   m_AddDel.signal_user_requested_edit().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_requested_edit));
-  m_AddDel.signal_user_requested_delete().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_requested_delete));
-  m_AddDel.signal_user_added().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_added));
-  m_AddDel.signal_user_changed().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_changed));
   m_AddDel.signal_script_button_clicked().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_script_button_clicked));
-  m_AddDel.signal_user_reordered_columns().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_reordered_columns));
+  
+  //TODO: Re-add this signal if this is really wanted, but make it part of a complete drag-and-drop feature for list views:
+  //m_AddDel.signal_user_reordered_columns().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_reordered_columns));
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
   m_AddDel.signal_user_requested_layout().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_requested_layout));
@@ -169,82 +166,11 @@
         m_AddDel.select_item(refModel->children().begin());
 
     } //privs
-
-    fill_end();
   }
 
   return result;
 }
 
-void Box_Data_List::on_adddel_user_requested_add()
-{
-  //Don't try to add a record to a list with no fields.
- 
-  //This isn't enough as a quick check, because the primary key is always in this list: if(m_FieldsShown.empty())
-  //{
-    //Warn the user that they won't see anything if there are no fields on the layout,
-    //doing an extra check:
-    //TODO_performance: Maybe this slows down the response when clicking Add.
-    Document_Glom* document = get_document();
-    if( document && !(document->get_data_layout_groups_have_any_fields(m_layout_name, m_table_name)) )
-    {
-      Gtk::Window* parent_window = get_app_window();
-      if(parent_window)
-        Utils::show_ok_dialog(_("Layout Contains No Fields"), _("There are no fields on the layout, so there is no way to enter data in a new record."), *parent_window, Gtk::MESSAGE_ERROR);
-
-      return;
-    }
-  //}
-
-  Gtk::TreeModel::iterator iter = m_AddDel.get_item_placeholder();
-  if(iter)
-  {
-    sharedptr<LayoutItem_Field> fieldToEdit;
-
-    //Start editing in the primary key or the first cell if the primary key is auto-incremented (because there is no point in editing an auto-generated value)
-    guint index_primary_key = 0;
-    const bool bPresent = get_field_primary_key_index(index_primary_key); //If there is no primary key then the default of 0 is OK.
-    if(bPresent)
-    {
-      sharedptr<Field> fieldPrimaryKey = get_field_primary_key();
-      if(fieldPrimaryKey && fieldPrimaryKey->get_auto_increment())
-      {
-        //Start editing in the first cell that is not auto_increment:
-        for(type_vecLayoutFields::const_iterator iter = m_FieldsShown.begin(); iter != m_FieldsShown.end(); ++iter)
-        {
-          sharedptr<LayoutItem_Field> layout_item = *iter;
-          if(!(layout_item->get_full_field_details()->get_auto_increment()))
-          {
-            fieldToEdit = layout_item;
-            break;
-          }
-        }
-      }
-      else
-      {
-        //The primary key is not auto-increment, so start by editing it:
-        fieldToEdit = sharedptr<LayoutItem_Field>::create();
-        fieldToEdit->set_full_field_details(fieldPrimaryKey);
-      }
-    }
-
-    //std::cout << "debug: index_field_to_edit=" << index_field_to_edit << std::endl;
-
-    if(fieldToEdit)
-    {
-      m_AddDel.select_item(iter, fieldToEdit, true /* start_editing */);
-    }
-    else
-    {
-      std::cout << "on_adddel_user_requested_add(): no editable rows." << std::endl;
-      //The only keys are non-editable, so just add a row:
-      on_adddel_user_added(iter, 0);
-      m_AddDel.select_item(iter); //without start_editing.
-      //g_warning("Box_Data_List::on_adddel_user_requested_add(): index_field_to_edit does not exist: %d", index_field_to_edit);
-    }
-  }
-}
-
 void Box_Data_List::on_adddel_user_requested_edit(const Gtk::TreeModel::iterator& row)
 {
   const Gnome::Gda::Value primary_key_value = m_AddDel.get_value_key(row); //The primary key is in the key.
@@ -259,105 +185,12 @@
 }
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
-void Box_Data_List::on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator&  /* rowEnd TODO */)
-{
-  if(rowStart)
-  {
-    if(confirm_delete_record())
-    {
-      const Gnome::Gda::Value primary_key_value = get_primary_key_value(rowStart);
-      record_delete(primary_key_value);
-
-      //Remove the row:
-      m_AddDel.remove_item(rowStart);
-
-      on_record_deleted(primary_key_value);
-    }
-  }
-}
-
 
 void Box_Data_List::set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value)
 {
   m_AddDel.set_value_key(row, value);
 }
 
-void Box_Data_List::on_adddel_user_added(const Gtk::TreeModel::iterator& row, guint /* col_with_first_value */)
-{
-  //std::cout << "Box_Data_List::on_adddel_user_added" << std::endl;
-
-  Gnome::Gda::Value primary_key_value;
-
-  sharedptr<Field> primary_key_field = m_AddDel.get_key_field();
-
-  //Get the new primary key value, if one is available now:
-  if(primary_key_field->get_auto_increment())
-  {
-    //Auto-increment is awkward (we can't get the last-generated ID) with postgres, so we auto-generate it ourselves;
-    const Glib::ustring& strPrimaryKeyName = primary_key_field->get_name();
-    primary_key_value = generate_next_auto_increment(m_table_name, strPrimaryKeyName);  //TODO: return a Gnome::Gda::Value of an appropriate type.
-  }
-  else
-  {
-    //Use the user-entered primary key value:
-
-    //This only works when the primary key is already stored: primary_key_value = get_primary_key_value(row);
-    primary_key_value = get_entered_field_data_field_only(primary_key_field);
-  }
-
-  //If no primary key value is available yet, then don't add the record yet:
-  if(!Conversions::value_is_empty(primary_key_value))
-  {
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-    sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window()); //Keep it alive while we need the data_model.
-#else
-    std::auto_ptr<ExceptionConnection> error;
-    sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window(), error); //Keep it alive while we need the data_model.
-    // Ignore error, sharedconnection presence is checked below
-#endif
-    if(sharedconnection)
-    {
-      sharedptr<LayoutItem_Field> layout_field = sharedptr<LayoutItem_Field>::create();
-      layout_field->set_full_field_details(primary_key_field);
-      if(!check_entered_value_for_uniqueness(m_table_name, layout_field, primary_key_value, get_app_window()))
-      {
-        //Revert to a blank value.
-        primary_key_value = Conversions::get_empty_value(layout_field->get_full_field_details()->get_glom_type());
-        set_entered_field_data(row, layout_field, primary_key_value);
-        return;
-      }
-
-      Glib::RefPtr<Gnome::Gda::DataModel> data_model = record_new(true /* use entered field data*/, primary_key_value, row);
-      if(data_model)
-      {
-        
-        //Save the primary key value for later use:
-        //record_new() did this: m_AddDel.set_value_key(row, primary_key_value);
-
-        //Show the primary key in the row, if the primary key is visible:
-
-        //If it's an auto-increment, then get the value and show it:
-        if(primary_key_field->get_auto_increment())
-        {
-          sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-          layout_item->set_full_field_details(primary_key_field);
-          m_AddDel.set_value(row, layout_item, primary_key_value);
-        }
-
-        on_record_added(primary_key_value, row);
-      }
-      else
-        handle_error();
-    }
-    else
-    {
-      //Add Record failed.
-      //Replace with correct values:
-      fill_from_database();
-    }
-  }
-}
-
 void Box_Data_List::on_adddel_user_reordered_columns()
 {
   Document_Glom* pDoc = dynamic_cast<Document_Glom*>(get_document());
@@ -382,164 +215,34 @@
   }
 }
 
-void Box_Data_List::on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col)
-{
-  const Gnome::Gda::Value parent_primary_key_value = get_primary_key_value(row);
-  sharedptr<const LayoutItem_Field> layout_field = m_AddDel.get_column_field(col);
-
-  if(!Conversions::value_is_empty(parent_primary_key_value)) //If the record's primary key is filled in:
-  {
-    //Just update the record:
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-    try
-#endif // GLIBMM_EXCEPTIONS_ENABLED
-    {
-      
-      Glib::ustring table_name = m_table_name;
-      sharedptr<Field> primary_key_field;
-      Gnome::Gda::Value primary_key_value;
-
-      if(!layout_field->get_has_relationship_name())
-      {
-        table_name = m_table_name;
-        primary_key_field = m_AddDel.get_key_field();
-        primary_key_value = parent_primary_key_value;
-      }
-      else
-      {
-        //If it's a related field then discover the actual table that it's in,
-        //plus how to identify the record in that table.
-        const Glib::ustring relationship_name = layout_field->get_relationship_name();
-
-        Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
-
-        sharedptr<Relationship> relationship = document->get_relationship(m_table_name, relationship_name);
-        if(relationship)
-        {
-          table_name = relationship->get_to_table();
-          const Glib::ustring to_field_name = relationship->get_to_field();
-          //Get the key field in the other table (the table that we will change)
-          primary_key_field = get_fields_for_table_one_field(table_name, to_field_name); //TODO_Performance.
-          if(primary_key_field)
-          {
-            //Get the value of the corresponding key in the current table (that identifies the record in the table that we will change)
-            sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-            layout_item->set_full_field_details( document->get_field(relationship->get_from_table(), relationship->get_from_field()) );
-
-            primary_key_value = get_entered_field_data(layout_item);
-
-            //Note: This just uses an existing record if one already exists:
-            Gnome::Gda::Value primary_key_value_used;
-            const bool test = add_related_record_for_field(layout_field, relationship, primary_key_field, primary_key_value, primary_key_value_used);
-            if(!test)
-              return;
-
-            //Get the new primary_key_value if it has been created:
-            primary_key_value = primary_key_value_used;
-
-            //Now that the related record exists, the following code to set the value of the other field in the related field can succeed.
-          }
-          else
-          {
-            g_warning("Box_Data_List::on_flowtable_field_edited(): key not found for edited related field.");
-          }
-        }
-      }
-
-      //Update the field in the record (the record with this primary key):
-      const Gnome::Gda::Value field_value = m_AddDel.get_value(row, layout_field);
-      //std::cout << "Box_Data_List::on_adddel_user_changed(): field_value = " << field_value.to_string() << std::endl;
-      //const sharedptr<const Field>& field = layout_field->m_field;
-      //const Glib::ustring strFieldName = layout_field->get_name();
-
-      LayoutFieldInRecord field_in_record(layout_field, m_table_name /* parent */, primary_key_field, primary_key_value);
-
-      //Check whether the value meets uniqueness constraints:
-      Gtk::Window* window = get_app_window();
-      if(!check_entered_value_for_uniqueness(m_table_name, row, layout_field, field_value, window))
-      {
-        //Revert to the value in the database:
-        const Gnome::Gda::Value value_old = get_field_value_in_database(field_in_record, window);
-        set_entered_field_data(row, layout_field, value_old);
-
-        return; //The value has been reverted to the value in the database.
-      }
-
-
-      const bool bTest = set_field_value_in_database(field_in_record, row, field_value, false /* don't use current calculations */, window);
-
-      //Glib::ustring strQuery = "UPDATE \"" + table_name + "\"";
-      //strQuery += " SET " +  /* table_name + "." + postgres does not seem to like the table name here */ strFieldName + " = " + field.sql(field_value);
-      //strQuery += " WHERE " + table_name + "." + primary_key_field.get_name() + " = " + primary_key_field.sql(primary_key_value);
-      //bool bTest = query_execute(strQuery);
-      if(!bTest)
-      {
-        //Update failed.
-        fill_from_database(); //Replace with correct values.
-      }
-    }
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-    catch(const Glib::Exception& ex)
-    {
-      handle_error(ex);
-    }
-    catch(const std::exception& ex)
-    {
-      handle_error(ex);
-    }
-#endif // GLIBMM_EXCEPTIONS_ENABLED
-  }
-  else
-  {
-    //This record probably doesn't exist yet.
-    //Add new record, which will generate the primary key:
-    //Actually, on_adddel_user_added() is usually just called directly in response to the user_added signal.
-    on_adddel_user_added(row, col);
-    
-    const Gnome::Gda::Value primaryKeyValue = get_primary_key_value(row); //TODO_Value
-    if(!(Conversions::value_is_empty(primaryKeyValue))) //If the Add succeeeded:
-    {
-      if(!(layout_field->get_full_field_details()->get_primary_key())) //Don't try to re-set the primary key field, because we just inserted the record with it.
-      {
-        on_adddel_user_changed(row, col); //Change this field in the new record.
-      }
-    }
-    else
-    {
-      //A field value was entered, but the record has not been added yet, because not enough information exists yet.
-       g_warning("Box_Data_List::on_adddel_user_changed(): debug: record not yet added.");
-    }
-  }
-
-}
-
 void Box_Data_List::on_adddel_script_button_clicked(const sharedptr<const LayoutItem_Button>& layout_item, const Gtk::TreeModel::iterator& row)
 {
-  if(layout_item)
-  {
-    const Gnome::Gda::Value primary_key_value = get_primary_key_value(row);
-    execute_button_script(layout_item, primary_key_value);
+  if(!layout_item)
+    return;
+  
+  const Gnome::Gda::Value primary_key_value = get_primary_key_value(row);
+  execute_button_script(layout_item, primary_key_value);
 
-    // Refill view from database as the script might have changed arbitrary records
+  // Refill view from database as the script might have changed arbitrary records
 
 #if 0
-    // TODO: This is perhaps a better approach, but
-    // DbTreeModel::refresh_from_database is protected
-    Glib::RefPtr<Gtk::TreeModel> model = m_AddDel.get_model();
-    Glib::RefPtr<DbTreeModel> db_model = Glib::RefPtr<DbTreeModel>::cast_dynamic(model);
-    if(db_model) db_model->refresh_from_database(m_found_set);
+  // TODO: This is perhaps a better approach, but
+  // DbTreeModel::refresh_from_database is protected
+  Glib::RefPtr<Gtk::TreeModel> model = m_AddDel.get_model();
+  Glib::RefPtr<DbTreeModel> db_model = Glib::RefPtr<DbTreeModel>::cast_dynamic(model);
+  if(db_model)
+    db_model->refresh_from_database(m_found_set);
 #endif
 
-    // TODO: Calling refresh_data_from_database() causes a crash somewhere
-    // down in GTK+, so it is done in a handler here.
-    // We are currently in a callback from the CellRendererButton_Text cell
-    // renderer which is deleted by a call to refresh_data_from_database().
-    // Probably this causes issues somewhere. 
-    Glib::signal_idle().connect(sigc::bind(sigc::mem_fun(*this, &Box_Data_List::on_script_button_idle), primary_key_value));
+  // TODO: Calling refresh_data_from_database() causes a crash somewhere
+  // down in GTK+, so it is done in a handler here.
+  // We are currently in a callback from the CellRendererButton_Text cell
+  // renderer which is deleted by a call to refresh_data_from_database().
+  // Probably this causes issues somewhere. 
+  Glib::signal_idle().connect(sigc::bind(sigc::mem_fun(*this, &Box_Data_List::on_script_button_idle), primary_key_value));
 
-    //refresh_data_from_database();
-    //set_primary_key_value_selected(primary_key);
-  }
+  //refresh_data_from_database();
+  //set_primary_key_value_selected(primary_key);
 }
 
 bool Box_Data_List::on_script_button_idle(const Gnome::Gda::Value& primary_key)
@@ -638,17 +341,17 @@
   }
 }
 
-Gnome::Gda::Value Box_Data_List::get_primary_key_value(const Gtk::TreeModel::iterator& row)
+Gnome::Gda::Value Box_Data_List::get_primary_key_value(const Gtk::TreeModel::iterator& row) const
 {
   return m_AddDel.get_value_key(row);
 }
 
-Gnome::Gda::Value Box_Data_List::get_primary_key_value_selected()
+Gnome::Gda::Value Box_Data_List::get_primary_key_value_selected() const
 {
   return m_AddDel.get_value_key_selected();
 }
 
-Gnome::Gda::Value Box_Data_List::get_primary_key_value_first()
+Gnome::Gda::Value Box_Data_List::get_primary_key_value_first() const
 {
   //std::cout << "Box_Data_List(): get_primary_key_value_first() records_count = " << m_AddDel.get_count() << std::endl;
 
@@ -808,32 +511,11 @@
 
 }
 
-void Box_Data_List::on_record_deleted(const Gnome::Gda::Value& /* primary_key_value */)
-{
-   //Overridden by Box_Data_List_Related.
-}
-
-void Box_Data_List::on_record_added(const Gnome::Gda::Value& /* strPrimaryKey */, const Gtk::TreeModel::iterator& /* row */)
-{
-  //Overridden by Box_Data_List_Related.
-  //m_AddDel.add_item(strPrimaryKey); //Add blank row.
-}
-
-Box_Data_List::type_signal_user_requested_details Box_Data_List::signal_user_requested_details()
-{
-  return m_signal_user_requested_details;
-}
-
 sharedptr<Field> Box_Data_List::get_field_primary_key() const
 {
   return m_AddDel.get_key_field();
 }
 
-bool Box_Data_List::get_field_primary_key_index(guint& field_column) const
-{
-  return Box_Data::get_field_primary_key_index_for_fields(m_FieldsShown, field_column);
-}
-
 void Box_Data_List::print_layout()
 {
   const Privileges table_privs = Privs::get_current_privs(m_table_name);

Modified: trunk/glom/mode_data/box_data_list.h
==============================================================================
--- trunk/glom/mode_data/box_data_list.h	(original)
+++ trunk/glom/mode_data/box_data_list.h	Tue Apr 29 14:32:35 2008
@@ -23,13 +23,13 @@
 
 #include "config.h" // GLOM_ENABLE_CLIENT_ONLY
 
-#include "box_data.h"
+#include "box_data_manyrecords.h"
 #include "../utility_widgets/db_adddel/db_adddel_withbuttons.h"
 
 namespace Glom
 {
 
-class Box_Data_List : public Box_Data
+class Box_Data_List : public Box_Data_ManyRecords
 {
 public: 
   Box_Data_List();
@@ -37,11 +37,10 @@
 
   void refresh_data_from_database_blank();
 
-  virtual Gnome::Gda::Value get_primary_key_value(const Gtk::TreeModel::iterator& row);
-  virtual Gnome::Gda::Value get_primary_key_value_selected();
-  Gnome::Gda::Value get_primary_key_value_first();
+  Gnome::Gda::Value get_primary_key_value_first() const;
 
-  ///This allows Box_Data::record_new() to set the generated/entered primary key value, needed by Box_Data_List:
+  //Implementations of pure virtual methods from Base_DB_Table_Data:
+  virtual Gnome::Gda::Value get_primary_key_value_selected() const;
   virtual void set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value);
 
   virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
@@ -58,9 +57,6 @@
   ///Highlight and scroll to the specified record, with primary key value @primary_key_value.
   void set_primary_key_value_selected(const Gnome::Gda::Value& primary_key_value);
 
-  //Primary Key value:
-  typedef sigc::signal<void, const Gnome::Gda::Value&> type_signal_user_requested_details;
-  type_signal_user_requested_details signal_user_requested_details();
 
   //Signal Handlers:
   virtual void on_details_nav_first();
@@ -75,34 +71,34 @@
   virtual void on_dialog_layout_hide(); //override
   #endif //GLOM_ENABLE_CLIENT_ONLY
 
+
 protected:
+
+  //Implementations of pure virtual methods from Base_DB_Table_Data:
+  virtual Gnome::Gda::Value get_primary_key_value(const Gtk::TreeModel::iterator& row) const;
+
+  //Overrides of functions from Box_Data:
   virtual void create_layout(); //override
-  virtual Document_Glom::type_list_layout_groups create_layout_get_layout(); //overriden in Box_Data_List_Related.
+  virtual Document_Glom::type_list_layout_groups create_layout_get_layout();
   void create_layout_add_group(const sharedptr<LayoutGroup>& layout_group);
+    
   virtual bool fill_from_database(); //override.
   virtual void enable_buttons();
 
-  virtual bool get_field_primary_key_index(guint& field_column) const; //TODO: visible 
   virtual sharedptr<Field> get_field_primary_key() const;
 
   //Signal handlers:
-  virtual void on_adddel_user_requested_add();
-  virtual void on_adddel_user_requested_edit(const Gtk::TreeModel::iterator& row);
-  virtual void on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
-  virtual void on_adddel_user_added(const Gtk::TreeModel::iterator& row, guint col_with_first_value);
-  virtual void on_adddel_user_reordered_columns();
+  void on_adddel_user_requested_edit(const Gtk::TreeModel::iterator& row);
+  void on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
+  void on_adddel_user_reordered_columns();
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
-  virtual void on_adddel_user_requested_layout();
+  void on_adddel_user_requested_layout();
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
-  virtual void on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col);
-  virtual void on_adddel_script_button_clicked(const sharedptr<const LayoutItem_Button>& layout_item, const Gtk::TreeModel::iterator& row);
+  void on_adddel_script_button_clicked(const sharedptr<const LayoutItem_Button>& layout_item, const Gtk::TreeModel::iterator& row);
   virtual bool on_script_button_idle(const Gnome::Gda::Value& primary_key_value);
 
-  virtual void on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row); //Not a signal handler. To be overridden.
-  virtual void on_record_deleted(const Gnome::Gda::Value& primary_key_value);
-
   virtual void print_layout();
   virtual void print_layout_group(xmlpp::Element* node_parent, const sharedptr<const LayoutGroup>& group);
 
@@ -111,15 +107,13 @@
   virtual void prepare_layout_dialog(Dialog_Layout* dialog); // override.
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
-  //Member widgers:
+  //Member widgets:
   mutable DbAddDel_WithButtons m_AddDel; //mutable because its get_ methods aren't const.
 
   bool m_has_one_or_more_records;
   bool m_read_only;
 
   bool m_reset_column_widths; //create_layout() sets these to 0 when this is set.
-
-  type_signal_user_requested_details m_signal_user_requested_details;
 };
 
 } //namespace Glom

Modified: trunk/glom/mode_data/box_data_list_related.cc
==============================================================================
--- trunk/glom/mode_data/box_data_list_related.cc	(original)
+++ trunk/glom/mode_data/box_data_list_related.cc	Tue Apr 29 14:32:35 2008
@@ -33,10 +33,22 @@
 {
   set_size_request(400, -1); //An arbitrary default.
 
-
-  remove(m_AddDel);
+  m_AddDel.set_rules_hint(); //Use alternating row colors when the theme does that.
   m_Alignment.add(m_AddDel);
+  m_AddDel.show();
+  m_Alignment.show();
+  
+  //Connect signals:  
+  m_AddDel.signal_user_requested_edit().connect(sigc::mem_fun(*this, &Box_Data_List_Related::on_adddel_user_requested_edit));
+  m_AddDel.signal_record_changed().connect(sigc::mem_fun(*this, &Box_Data_List_Related::on_adddel_record_changed));
 
+  m_AddDel.signal_script_button_clicked().connect(sigc::mem_fun(*this, &Box_Data_List_Related::on_adddel_script_button_clicked));
+  m_AddDel.signal_record_added().connect(sigc::mem_fun(*this, &Box_Data_List_Related::on_adddel_record_added));
+  
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  m_AddDel.signal_user_requested_layout().connect(sigc::mem_fun(*this, &Box_Data_List_Related::on_adddel_user_requested_layout));
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+  
   m_layout_name = "list_related"; //TODO: We need a unique name when 2 portals use the same table.
 }
 
@@ -51,7 +63,7 @@
   m_portal = glom_sharedptr_clone(portal);
 
   LayoutWidgetBase::m_table_name = m_portal->get_table_used(Glib::ustring() /* parent table_name, not used. */); 
-  Box_DB_Table::m_table_name = LayoutWidgetBase::m_table_name;
+  Base_DB_Table::m_table_name = LayoutWidgetBase::m_table_name;
 
   const Glib::ustring relationship_title = m_portal->get_title_used(Glib::ustring() /* parent title - not relevant */);
   
@@ -72,11 +84,16 @@
 
   m_key_field = get_fields_for_table_one_field(LayoutWidgetBase::m_table_name, m_portal->get_to_field_used());
 
+  //Prevent impossible multiple related records:
+  const bool single_related = (m_key_field && (m_key_field->get_unique_key() || m_key_field->get_primary_key()));
+  m_AddDel.set_allow_only_one_related_record(single_related);
+  
   enable_buttons();
 
   FoundSet found_set;
   found_set.m_table_name = LayoutWidgetBase::m_table_name;
-  return Box_Data_List::init_db_details(found_set); //Calls create_layout() and fill_from_database().
+  m_AddDel.set_found_set(found_set);
+  return Box_Data_ManyRecords::init_db_details(found_set); //Calls create_layout() and fill_from_database().
 }
 
 bool Box_Data_List_Related::fill_from_database()
@@ -88,19 +105,17 @@
   {
     //No Foreign Key value, so just show the field names:
 
-    result = Box_DB_Table::fill_from_database();
+    result = Base_DB_Table_Data::fill_from_database();
 
     //create_layout();
-
-    fill_end();
   }
   else
   {
-    result = Box_Data_List::fill_from_database();
+    result = Box_Data_Portal::fill_from_database();
 
 
     //Is there already one record here?
-    if(m_has_one_or_more_records) //This was set by Box_Data_List::fill_from_database().
+    if(m_has_one_or_more_records) //This was set by Box_Data_Portal::fill_from_database().
     {
       //Is the to_field unique? If so, there can not be more than one.
       if(m_key_field && m_key_field->get_unique_key()) //automatically true if it is a primary key
@@ -117,14 +132,93 @@
     allow_add = m_portal->get_relationship()->get_auto_create();
 
   m_AddDel.set_allow_add(allow_add);
+  
+  m_AddDel.set_found_set(m_found_set);
+  result = m_AddDel.refresh_from_database();
 
   return result;
 }
 
-void Box_Data_List_Related::on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row)
+Gnome::Gda::Value Box_Data_List_Related::get_primary_key_value(const Gtk::TreeModel::iterator& row) const
+{
+  return m_AddDel.get_value_key(row);
+}
+
+void Box_Data_List_Related::on_adddel_user_requested_edit(const Gtk::TreeModel::iterator& row)
+{
+  if(!(m_portal->get_relationship_used_allows_edit()))
+  {
+    std::cerr << "Box_Data_List_Related::on_adddel_user_requested_eidt() called on non-editable portal. This should not happen." << std::endl;
+    return;
+  }
+
+  //Call base class:
+  
+  const Gnome::Gda::Value primary_key_value = m_AddDel.get_value_key(row); //The primary key is in the key.
+  std::cout << "on_adddel_user_requested_edit(): Requesting edit for primary_key=" << primary_key_value.to_string() << std::endl;
+  signal_user_requested_details().emit(primary_key_value);
+}
+
+void Box_Data_List_Related::on_adddel_record_changed()
+{
+  //Let parent respond:
+  signal_portal_record_changed().emit(m_portal->get_relationship_name());
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+void Box_Data_List_Related::on_adddel_user_requested_layout()
+{
+  show_layout_dialog();
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+
+void Box_Data_List_Related::on_adddel_script_button_clicked(const sharedptr<const LayoutItem_Button>& layout_item, const Gtk::TreeModel::iterator& row)
 {
+  if(!layout_item)
+    return;
+  
+  const Gnome::Gda::Value primary_key_value = get_primary_key_value(row);
+  execute_button_script(layout_item, primary_key_value);
+
+  // Refill view from database as the script might have changed arbitrary records
+
+#if 0
+  // TODO: This is perhaps a better approach, but
+  // DbTreeModel::refresh_from_database is protected
+  Glib::RefPtr<Gtk::TreeModel> model = m_AddDel.get_model();
+  Glib::RefPtr<DbTreeModel> db_model = Glib::RefPtr<DbTreeModel>::cast_dynamic(model);
+  if(db_model)
+    db_model->refresh_from_database(m_found_set);
+#endif
+
+  // TODO: Calling refresh_data_from_database() causes a crash somewhere
+  // down in GTK+, so it is done in a handler here.
+  // We are currently in a callback from the CellRendererButton_Text cell
+  // renderer which is deleted by a call to refresh_data_from_database().
+  // Probably this causes issues somewhere. 
+  Glib::signal_idle().connect(sigc::bind(sigc::mem_fun(*this, &Box_Data_List_Related::on_script_button_idle), primary_key_value));
+
+  //refresh_data_from_database();
+  //set_primary_key_value_selected(primary_key);
+}
+
+bool Box_Data_List_Related::on_script_button_idle(const Gnome::Gda::Value& primary_key)
+{
+  refresh_data_from_database();
+  set_primary_key_value_selected(primary_key);
+  return false;
+}
+
+void Box_Data_List_Related::on_adddel_record_added(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& primary_key_value)
+{
+  //Note that on_record_added() would only be called on the AddDel itself, so we need ot handle this AddDel signal.
+  
   //primary_key_value is a new autogenerated or human-entered key for the row.
   //It has already been added to the database.
+  //Gnome::Gda::Value primary_key_value = m_AddDel.get_value_key(row);
+  std::cout << "Box_Data_List_Related::on_adddel_record_added(): primary_key_value=" << primary_key_value.to_string() << std::endl;
+
 
   if(!row)
     return;
@@ -138,8 +232,7 @@
     layout_item->set_full_field_details(m_key_field);
     key_value = m_AddDel.get_value(row, layout_item);
   }
-
-  Box_Data_List::on_record_added(key_value, row); //adds blank row.
+  
 
   //Make sure that the new related record is related,
   //by setting the foreign key:
@@ -151,7 +244,7 @@
   }
   else if(Conversions::value_is_empty(m_key_value))
   {
-    g_warning("Box_Data_List_Related::on_record_added(): m_key_value is NULL.");
+    g_warning("Box_Data_List_Related::on_adddel_record_added(): m_key_value is NULL.");
   }
   else
   {
@@ -163,79 +256,29 @@
       Glib::ustring strQuery = "UPDATE \"" + m_portal->get_table_used(Glib::ustring() /* not relevant */) + "\"";
       strQuery += " SET \"" +  /* get_table_name() + "." +*/ m_key_field->get_name() + "\" = " + m_key_field->sql(m_key_value);
       strQuery += " WHERE \"" + get_table_name() + "\".\"" + field_primary_key->get_name() + "\" = " + field_primary_key->sql(primary_key_value);
+      std::cout << "Box_Data_List_Related::on_adddel_record_added(): setting value in db=" << primary_key_value.to_string() << std::endl;
       const bool test = query_execute(strQuery, get_app_window());
       if(test)
       {
         //Show it on the view, if it's visible:
         sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-        layout_item->set_full_field_details(field_primary_key);
+        layout_item->set_full_field_details(m_key_field);
 
+        //TODO: Although the to-field value is visible on the new related record, get_value() returns NULL so you can't immediately navigate to the new record: 
+        //std::cout << "DEBUG: Box_Data_List_Related::on_record_added(): setting field=" << layout_item->get_name() << "m_key_value=" << m_key_value.to_string() << std::endl; 
         m_AddDel.set_value(row, layout_item, m_key_value);
       }
+      else
+        std::cerr << "Box_Data_List_Related::on_record_added(): SQL query failed: " << strQuery << std::endl;
     }
+    else
+      std::cerr << "Box_Data_List_Related::on_record_added(): m_key_field is NULL" << std::endl;
+  
 
     //on_adddel_user_changed(row, iKey); //Update the database.
   }
-}
-
-void Box_Data_List_Related::on_record_deleted(const Gnome::Gda::Value& /* primary_key_value */)
-{
-  //Allow the parent record (Details view) to recalculate aggregations:
-  signal_record_changed().emit(m_portal->get_relationship_name());
-}
-
-
-void Box_Data_List_Related::on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col)
-{
-  if(!(m_portal->get_relationship_used_allows_edit()))
-  {
-    std::cerr << "Box_Data_List_Related::on_adddel_user_added() called on non-editable portal. This should not happen." << std::endl;
-   return;
-  }
-
-  //Call base class:
-  Box_Data_List::on_adddel_user_changed(row, col);
-
-  //Let parent respond:
-  if(row)
-    signal_record_changed().emit(m_portal->get_relationship_name());
-}
-
-void Box_Data_List_Related::on_adddel_user_added(const Gtk::TreeModel::iterator& row, guint col_with_first_value)
-{
-  if(!m_portal->get_relationship_used_allows_edit())
-  {
-    std::cerr << "Box_Data_List_Related::on_adddel_user_added() called on non-editable portal. This should not happen." << std::endl;
-   return;
-  }
-
-  //Like Box_Data_List::on_adddel_user_added(),
-  //but it doesn't allow adding if the new record cannot be a related record.
-  //This would happen if there is already one related record and the relationship uses the primary key in the related record.
-
-  bool bAllowAdd = true;
-
-  if(m_key_field && (m_key_field->get_unique_key() || m_key_field->get_primary_key()))
-  {
-    if(m_AddDel.get_count() > 0) //If there is already 1 record
-      bAllowAdd = false;
-  }
-
-  if(bAllowAdd)
-  {
-    Box_Data_List::on_adddel_user_added(row, col_with_first_value);
-  }
-  else
-  {
-    //Tell user that they can't do that:
-    Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Extra related records not possible.")), true, Gtk::MESSAGE_WARNING);
-    dialog.set_secondary_text(_("You attempted to add a new related record, but there can only be one related record, because the relationship uses a unique key.")),
-    dialog.set_transient_for(*get_app_window());
-    dialog.run();
-
-    //Replace with correct values:
-    fill_from_database();
-  }
+  
+  on_record_added(key_value, row);
 }
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
@@ -260,14 +303,6 @@
 }
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
-void Box_Data_List_Related::on_adddel_user_requested_add()
-{
-  //Prevent an add on a portal with no fields:
-  //TODO: Warn the user instead of just doing nothing.
-  if(!m_portal->m_list_items.empty())
-    Box_Data_List::on_adddel_user_requested_add();
-}
-
 #ifndef GLOM_ENABLE_CLIENT_ONLY
 Dialog_Layout* Box_Data_List_Related::create_layout_dialog() const
 {
@@ -290,4 +325,136 @@
 }
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
+Gnome::Gda::Value Box_Data_List_Related::get_primary_key_value_selected() const
+{
+  return m_AddDel.get_value_key_selected();
+}
+
+sharedptr<Field> Box_Data_List_Related::get_field_primary_key() const
+{
+  return m_AddDel.get_key_field();
+}
+
+void Box_Data_List_Related::set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value)
+{
+  m_AddDel.set_value_key(row, value);
+}
+
+
+Document_Glom::type_list_layout_groups Box_Data_List_Related::create_layout_get_layout()
+{
+  //Overriden in Box_Data_List_Related:
+  return get_data_layout_groups(m_layout_name); 
+}
+
+//These create_layout*() methods are actually copy/pasted from Box_Data_List().
+//TODO: Reduce the copy/pasting of these?
+void Box_Data_List_Related::create_layout()
+{
+  Box_Data::create_layout(); //Fills m_TableFields.
+
+  const Document_Glom* pDoc = dynamic_cast<const Document_Glom*>(get_document());
+  if(pDoc)
+  {
+    //Field Names:
+    m_AddDel.remove_all_columns();
+    //m_AddDel.set_columns_count(m_Fields.size());
+
+    m_AddDel.set_table_name(Base_DB_Table::m_table_name);
+
+
+    sharedptr<Field> field_primary_key = get_field_primary_key_for_table(Base_DB_Table::m_table_name);
+    if(!field_primary_key)
+    {
+      //g_warning("%s: primary key not found.", __FUNCTION__);
+    }
+    else
+    {
+      m_AddDel.set_key_field(field_primary_key);
+ 
+      //This map of layout groups will also contain the field information from the database:
+      Document_Glom::type_list_layout_groups layout_groups = create_layout_get_layout();
+      
+      //int debug_count = 0;
+      for(Document_Glom::type_list_layout_groups::const_iterator iter = layout_groups.begin(); iter != layout_groups.end(); ++iter)
+      {
+        //std::cout << "Box_Data_List::create_layout() group number=" << debug_count;
+        //debug_count++;
+        //iter->second->debug();
+
+        create_layout_add_group(*iter);
+      }
+    }
+
+
+    m_FieldsShown = get_fields_to_show();
+
+    //Add extra possibly-non-visible columns that we need:
+    //TODO: Only add it if it is not already there.
+    if(field_primary_key)
+    {
+      sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
+      layout_item->set_hidden();
+      layout_item->set_full_field_details(m_AddDel.get_key_field());
+      m_FieldsShown.push_back(layout_item);
+
+      m_AddDel.add_column(layout_item);
+    }
+
+    m_AddDel.set_found_set(m_found_set);
+
+    //Column-creation happens in fill_database() instead:
+    //otherwise the treeview will be filled twice.
+    //m_AddDel.set_columns_ready();
+  }
+
+}
+
+void Box_Data_List_Related::create_layout_add_group(const sharedptr<LayoutGroup>& layout_group)
+{
+  if(!layout_group)
+    return;
+
+  LayoutGroup::type_list_items child_items = layout_group->get_items();
+  for(LayoutGroup::type_list_items::const_iterator iter = child_items.begin(); iter != child_items.end(); ++iter)
+  {
+    sharedptr<LayoutItem> child_item = *iter;
+
+    sharedptr<LayoutGroup> child_group = sharedptr<LayoutGroup>::cast_dynamic(child_item);
+    if(child_group)
+    {
+      //std::cout << "debug: Start Adding child group." << std::endl;
+      create_layout_add_group(child_group);
+      //std::cout << "debug: End Adding child group." << std::endl;
+    }
+    else
+    {
+      if(m_read_only)
+        child_item->set_editable(false);
+
+      //std::cout << "debug: adding column: " << child_item->get_name() << std::endl;
+
+      sharedptr<LayoutItem_Field> child_field = sharedptr<LayoutItem_Field>::cast_dynamic(child_item);
+      if(child_field)
+      {
+        //Check that the field really exists, to avoid SQL errors.
+        //This could probably only happen if we have failed to rename something everywhere, when the user has renamed something.
+        if(!get_field_exists_in_database(child_field->get_table_used(Base_DB_Table::m_table_name), child_field->get_name()))
+        {
+          std::cerr << "debug: Box_Data_List_Related::create_layout_add_group(): Field does not exist in database: table_name=" << child_field->get_table_used(Base_DB_Table::m_table_name) << ", field_name=" << child_field->get_name() << std::endl;
+          continue;
+        }
+      }
+
+      //Sometimes we reset the column width so that new fields are easily visible:
+      //if(m_reset_column_widths)
+      //  child_item->set_display_width(0);
+
+      m_AddDel.add_column(child_item);
+    }
+  }
+}
+
+
+
 } //namespace Glom

Modified: trunk/glom/mode_data/box_data_list_related.h
==============================================================================
--- trunk/glom/mode_data/box_data_list_related.h	(original)
+++ trunk/glom/mode_data/box_data_list_related.h	Tue Apr 29 14:32:35 2008
@@ -43,22 +43,46 @@
 protected:
   virtual bool fill_from_database(); //Override.
 
-  virtual void on_adddel_user_requested_add();
-  virtual void on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col);
-  virtual void on_adddel_user_added(const Gtk::TreeModel::iterator& row, guint col_with_first_value); //Override.
-  virtual void on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row); //Override. Not a signal handler.
-  virtual void on_record_deleted(const Gnome::Gda::Value& primary_key_value); //override.
+  //Signal handlers:
+  void on_adddel_record_changed();
+  void on_adddel_user_requested_edit(const Gtk::TreeModel::iterator& row);
+  void on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
+  void on_adddel_user_reordered_columns();
 
+  void on_adddel_script_button_clicked(const sharedptr<const LayoutItem_Button>& layout_item, const Gtk::TreeModel::iterator& row);
+  bool on_script_button_idle(const Gnome::Gda::Value& primary_key_value);
+
+  void on_adddel_record_added(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& primary_key_value);
+    
 #ifndef GLOM_ENABLE_CLIENT_ONLY
-  virtual void on_dialog_layout_hide(); //override.
+  void on_adddel_user_requested_layout();
 #endif // !GLOM_ENABLE_CLIENT_ONLY
-
+    
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  virtual void on_dialog_layout_hide(); //override.
+#endif // !GLOM_ENABLE_CLIENT_ONLY 
+  
+  //Implementations of pure virtual methods from Base_DB_Table_Data:
+  virtual sharedptr<Field> get_field_primary_key() const; //TODO: Already in base class?
+  virtual Gnome::Gda::Value get_primary_key_value_selected() const;
+  virtual void set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value);
+  virtual Gnome::Gda::Value get_primary_key_value(const Gtk::TreeModel::iterator& row) const;
+ 
+  //Overrides of functions from Box_Data:
+  virtual void create_layout(); //override
+  virtual Document_Glom::type_list_layout_groups create_layout_get_layout();
+  void create_layout_add_group(const sharedptr<LayoutGroup>& layout_group);
+    
   virtual void enable_buttons(); //override
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
   virtual Dialog_Layout* create_layout_dialog() const; // override.
   virtual void prepare_layout_dialog(Dialog_Layout* dialog); // override.
 #endif // !GLOM_ENABLE_CLIENT_ONLY
+    
+    
+  //Member widgets:
+  mutable DbAddDel_WithButtons m_AddDel; //mutable because its get_ methods aren't const.
 };
 
 } //namespace Glom

Copied: trunk/glom/mode_data/box_data_manyrecords.cc (from r1587, /trunk/glom/mode_data/box_data_list.cc)
==============================================================================
--- /trunk/glom/mode_data/box_data_list.cc	(original)
+++ trunk/glom/mode_data/box_data_manyrecords.cc	Tue Apr 29 14:32:35 2008
@@ -18,823 +18,51 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#include "box_data_list.h"
+#include "box_data_manyrecords.h"
 #include <glom/libglom/data_structure/glomconversions.h>
 #include <glom/libglom/glade_utils.h>
 #include <glom/reports/report_builder.h>
 #include "dialog_layout_list.h"
 #include <glom/glom_privs.h>
 #include <bakery/App/App_Gtk.h> //For util_bold_message().
-//#include <../utility_widgets/db_adddel/glom_db_treemodel.h> //For DbTreeModel.
 #include <sstream> //For stringstream
 #include <glibmm/i18n.h>
 
 namespace Glom
 {
 
-Box_Data_List::Box_Data_List()
+Box_Data_ManyRecords::Box_Data_ManyRecords()
 : m_has_one_or_more_records(false),
-  m_read_only(false),
-  m_reset_column_widths(false)
+  m_read_only(false)
 {
-  m_layout_name = "list";
-
-  //m_strHint = _("When you change the data in a field the database is updated immediately.\n Click [Add] or enter data into the last row to add a new record.\n Leave automatic ID fields empty - they will be filled for you.\nOnly the first 100 records are shown.");
-
-  pack_start(m_AddDel);
-  add_view(&m_AddDel); //Give it access to the document.
-  m_AddDel.set_auto_add(false); //We want to add the row ourselves when the user clicks the Add button, because the default behaviour there is not suitable.
-  m_AddDel.set_rules_hint(); //Use alternating row colors when the theme does that.
-
-  //Connect signals:
-  m_AddDel.signal_user_requested_add().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_requested_add)); //Only emitted when m_AddDel.set_auto_add(false) is used.
-  m_AddDel.signal_user_requested_edit().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_requested_edit));
-  m_AddDel.signal_user_requested_delete().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_requested_delete));
-  m_AddDel.signal_user_added().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_added));
-  m_AddDel.signal_user_changed().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_changed));
-  m_AddDel.signal_script_button_clicked().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_script_button_clicked));
-  m_AddDel.signal_user_reordered_columns().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_reordered_columns));
-
-#ifndef GLOM_ENABLE_CLIENT_ONLY
-  m_AddDel.signal_user_requested_layout().connect(sigc::mem_fun(*this, &Box_Data_List::on_adddel_user_requested_layout));
-#endif // !GLOM_ENABLE_CLIENT_ONLY
-
+  m_layout_name = "manyrecords"; //Set by derived classes.
 
   //Groups are not very helpful for a list view:
   //m_pDialogLayout->set_show_groups(false);
 
-  m_AddDel.show();
-}
-
-Box_Data_List::~Box_Data_List()
-{
-  remove_view(&m_AddDel);
-}
-
-void Box_Data_List::enable_buttons()
-{
-  const Privileges table_privs = Privs::get_current_privs(m_table_name);
-
-    //Enable/Disable record creation and deletion:
-  bool allow_create = !m_read_only;
-  bool allow_delete = !m_read_only;
-  if(!m_read_only)
-  {
-    allow_create = table_privs.m_create;
-    allow_delete = table_privs.m_delete;
-  }
-
-  m_AddDel.set_allow_add(allow_create);
-  m_AddDel.set_allow_delete(allow_delete);
-
-  m_AddDel.set_allow_view_details(table_privs.m_view);
-}
-
-void Box_Data_List::refresh_data_from_database_blank()
-{
-  FoundSet found_set = m_found_set;
-  found_set.m_where_clause = Glib::ustring();
-  m_AddDel.set_found_set(found_set);
-
-  std::cout << "debug: Box_Data_List::refresh_data_from_database_blank(): before refresh_from_database_blank()." << std::endl;
-  m_AddDel.refresh_from_database_blank();
-  m_found_set = found_set;
-}
-
-bool Box_Data_List::fill_from_database()
-{
-  bool result = false;
-
-  //Don't try to open a connection if there is no document,
-  //for instance, during application destruction.
-  if(!get_document())
-    return false;
-
-  Bakery::BusyCursor busy_cursor(get_app_window());
-
-  sharedptr<SharedConnection> sharedconnection;
-
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-  try
-  {
-    sharedconnection = connect_to_server(get_app_window());
-  }
-  catch(const Glib::Exception& ex)
-  {
-    handle_error(ex);
-    result = false;
-  }
-  catch(const std::exception& ex)
-  {
-    handle_error(ex);
-    result = false;
-  }
-#else
-  std::auto_ptr<ExceptionConnection> error;
-  sharedconnection = connect_to_server(get_app_window(), error);
-  if(error.get())
-  {
-    handle_error(*error);
-    result = false;
-  }
-#endif
-
-  if(sharedconnection)
-  {
-    Box_Data::fill_from_database();
-
-    //Field Names:
-    //create_layout();
-
-    //if(sharedconnection)
-    //{
-      //Glib::RefPtr<Gnome::Gda::Connection> connection = sharedconnection->get_gda_connection();
-
-    //Do not try to show the data if the user may not view it:
-    const Privileges table_privs = Privs::get_current_privs(m_table_name);
-
-    enable_buttons();
-
-    m_AddDel.set_found_set(m_found_set);
-
-    result = m_AddDel.refresh_from_database();
-
-    if(table_privs.m_view)
-    {
-      //TODO: Don't show it if m_view is false.
-
-      //Select first record:
-      Glib::RefPtr<Gtk::TreeModel> refModel = m_AddDel.get_model();
-      if(refModel)
-        m_AddDel.select_item(refModel->children().begin());
-
-    } //privs
-
-    fill_end();
-  }
-
-  return result;
-}
-
-void Box_Data_List::on_adddel_user_requested_add()
-{
-  //Don't try to add a record to a list with no fields.
- 
-  //This isn't enough as a quick check, because the primary key is always in this list: if(m_FieldsShown.empty())
-  //{
-    //Warn the user that they won't see anything if there are no fields on the layout,
-    //doing an extra check:
-    //TODO_performance: Maybe this slows down the response when clicking Add.
-    Document_Glom* document = get_document();
-    if( document && !(document->get_data_layout_groups_have_any_fields(m_layout_name, m_table_name)) )
-    {
-      Gtk::Window* parent_window = get_app_window();
-      if(parent_window)
-        Utils::show_ok_dialog(_("Layout Contains No Fields"), _("There are no fields on the layout, so there is no way to enter data in a new record."), *parent_window, Gtk::MESSAGE_ERROR);
-
-      return;
-    }
-  //}
-
-  Gtk::TreeModel::iterator iter = m_AddDel.get_item_placeholder();
-  if(iter)
-  {
-    sharedptr<LayoutItem_Field> fieldToEdit;
-
-    //Start editing in the primary key or the first cell if the primary key is auto-incremented (because there is no point in editing an auto-generated value)
-    guint index_primary_key = 0;
-    const bool bPresent = get_field_primary_key_index(index_primary_key); //If there is no primary key then the default of 0 is OK.
-    if(bPresent)
-    {
-      sharedptr<Field> fieldPrimaryKey = get_field_primary_key();
-      if(fieldPrimaryKey && fieldPrimaryKey->get_auto_increment())
-      {
-        //Start editing in the first cell that is not auto_increment:
-        for(type_vecLayoutFields::const_iterator iter = m_FieldsShown.begin(); iter != m_FieldsShown.end(); ++iter)
-        {
-          sharedptr<LayoutItem_Field> layout_item = *iter;
-          if(!(layout_item->get_full_field_details()->get_auto_increment()))
-          {
-            fieldToEdit = layout_item;
-            break;
-          }
-        }
-      }
-      else
-      {
-        //The primary key is not auto-increment, so start by editing it:
-        fieldToEdit = sharedptr<LayoutItem_Field>::create();
-        fieldToEdit->set_full_field_details(fieldPrimaryKey);
-      }
-    }
-
-    //std::cout << "debug: index_field_to_edit=" << index_field_to_edit << std::endl;
-
-    if(fieldToEdit)
-    {
-      m_AddDel.select_item(iter, fieldToEdit, true /* start_editing */);
-    }
-    else
-    {
-      std::cout << "on_adddel_user_requested_add(): no editable rows." << std::endl;
-      //The only keys are non-editable, so just add a row:
-      on_adddel_user_added(iter, 0);
-      m_AddDel.select_item(iter); //without start_editing.
-      //g_warning("Box_Data_List::on_adddel_user_requested_add(): index_field_to_edit does not exist: %d", index_field_to_edit);
-    }
-  }
-}
-
-void Box_Data_List::on_adddel_user_requested_edit(const Gtk::TreeModel::iterator& row)
-{
-  const Gnome::Gda::Value primary_key_value = m_AddDel.get_value_key(row); //The primary key is in the key.
-
-  signal_user_requested_details().emit(primary_key_value);
-}
-
-#ifndef GLOM_ENABLE_CLIENT_ONLY
-void Box_Data_List::on_adddel_user_requested_layout()
-{
-  show_layout_dialog();
-}
-#endif // !GLOM_ENABLE_CLIENT_ONLY
-
-void Box_Data_List::on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator&  /* rowEnd TODO */)
-{
-  if(rowStart)
-  {
-    if(confirm_delete_record())
-    {
-      const Gnome::Gda::Value primary_key_value = get_primary_key_value(rowStart);
-      record_delete(primary_key_value);
-
-      //Remove the row:
-      m_AddDel.remove_item(rowStart);
-
-      on_record_deleted(primary_key_value);
-    }
-  }
-}
-
-
-void Box_Data_List::set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value)
-{
-  m_AddDel.set_value_key(row, value);
-}
-
-void Box_Data_List::on_adddel_user_added(const Gtk::TreeModel::iterator& row, guint /* col_with_first_value */)
-{
-  //std::cout << "Box_Data_List::on_adddel_user_added" << std::endl;
-
-  Gnome::Gda::Value primary_key_value;
-
-  sharedptr<Field> primary_key_field = m_AddDel.get_key_field();
-
-  //Get the new primary key value, if one is available now:
-  if(primary_key_field->get_auto_increment())
-  {
-    //Auto-increment is awkward (we can't get the last-generated ID) with postgres, so we auto-generate it ourselves;
-    const Glib::ustring& strPrimaryKeyName = primary_key_field->get_name();
-    primary_key_value = generate_next_auto_increment(m_table_name, strPrimaryKeyName);  //TODO: return a Gnome::Gda::Value of an appropriate type.
-  }
-  else
-  {
-    //Use the user-entered primary key value:
-
-    //This only works when the primary key is already stored: primary_key_value = get_primary_key_value(row);
-    primary_key_value = get_entered_field_data_field_only(primary_key_field);
-  }
-
-  //If no primary key value is available yet, then don't add the record yet:
-  if(!Conversions::value_is_empty(primary_key_value))
-  {
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-    sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window()); //Keep it alive while we need the data_model.
-#else
-    std::auto_ptr<ExceptionConnection> error;
-    sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window(), error); //Keep it alive while we need the data_model.
-    // Ignore error, sharedconnection presence is checked below
-#endif
-    if(sharedconnection)
-    {
-      sharedptr<LayoutItem_Field> layout_field = sharedptr<LayoutItem_Field>::create();
-      layout_field->set_full_field_details(primary_key_field);
-      if(!check_entered_value_for_uniqueness(m_table_name, layout_field, primary_key_value, get_app_window()))
-      {
-        //Revert to a blank value.
-        primary_key_value = Conversions::get_empty_value(layout_field->get_full_field_details()->get_glom_type());
-        set_entered_field_data(row, layout_field, primary_key_value);
-        return;
-      }
-
-      Glib::RefPtr<Gnome::Gda::DataModel> data_model = record_new(true /* use entered field data*/, primary_key_value, row);
-      if(data_model)
-      {
-        
-        //Save the primary key value for later use:
-        //record_new() did this: m_AddDel.set_value_key(row, primary_key_value);
-
-        //Show the primary key in the row, if the primary key is visible:
-
-        //If it's an auto-increment, then get the value and show it:
-        if(primary_key_field->get_auto_increment())
-        {
-          sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-          layout_item->set_full_field_details(primary_key_field);
-          m_AddDel.set_value(row, layout_item, primary_key_value);
-        }
-
-        on_record_added(primary_key_value, row);
-      }
-      else
-        handle_error();
-    }
-    else
-    {
-      //Add Record failed.
-      //Replace with correct values:
-      fill_from_database();
-    }
-  }
-}
-
-void Box_Data_List::on_adddel_user_reordered_columns()
-{
-  Document_Glom* pDoc = dynamic_cast<Document_Glom*>(get_document());
-  if(pDoc)
-  {
-    sharedptr<LayoutGroup> group = sharedptr<LayoutGroup>::create();
-    group->set_name("toplevel");
-
-    AddDel::type_vecStrings vec_field_names = m_AddDel.get_columns_order();
-
-    for(AddDel::type_vecStrings::iterator iter = vec_field_names.begin(); iter != vec_field_names.end(); ++iter)
-    {
-      sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-      layout_item->set_name(*iter);
-      group->add_item(layout_item); 
-    }
-
-    Document_Glom::type_list_layout_groups mapGroups;
-    mapGroups[1] = group;
-
-    pDoc->set_data_layout_groups("list", m_table_name, mapGroups);  
-  }
-}
-
-void Box_Data_List::on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col)
-{
-  const Gnome::Gda::Value parent_primary_key_value = get_primary_key_value(row);
-  sharedptr<const LayoutItem_Field> layout_field = m_AddDel.get_column_field(col);
-
-  if(!Conversions::value_is_empty(parent_primary_key_value)) //If the record's primary key is filled in:
-  {
-    //Just update the record:
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-    try
-#endif // GLIBMM_EXCEPTIONS_ENABLED
-    {
-      
-      Glib::ustring table_name = m_table_name;
-      sharedptr<Field> primary_key_field;
-      Gnome::Gda::Value primary_key_value;
-
-      if(!layout_field->get_has_relationship_name())
-      {
-        table_name = m_table_name;
-        primary_key_field = m_AddDel.get_key_field();
-        primary_key_value = parent_primary_key_value;
-      }
-      else
-      {
-        //If it's a related field then discover the actual table that it's in,
-        //plus how to identify the record in that table.
-        const Glib::ustring relationship_name = layout_field->get_relationship_name();
-
-        Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
-
-        sharedptr<Relationship> relationship = document->get_relationship(m_table_name, relationship_name);
-        if(relationship)
-        {
-          table_name = relationship->get_to_table();
-          const Glib::ustring to_field_name = relationship->get_to_field();
-          //Get the key field in the other table (the table that we will change)
-          primary_key_field = get_fields_for_table_one_field(table_name, to_field_name); //TODO_Performance.
-          if(primary_key_field)
-          {
-            //Get the value of the corresponding key in the current table (that identifies the record in the table that we will change)
-            sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-            layout_item->set_full_field_details( document->get_field(relationship->get_from_table(), relationship->get_from_field()) );
-
-            primary_key_value = get_entered_field_data(layout_item);
-
-            //Note: This just uses an existing record if one already exists:
-            Gnome::Gda::Value primary_key_value_used;
-            const bool test = add_related_record_for_field(layout_field, relationship, primary_key_field, primary_key_value, primary_key_value_used);
-            if(!test)
-              return;
-
-            //Get the new primary_key_value if it has been created:
-            primary_key_value = primary_key_value_used;
-
-            //Now that the related record exists, the following code to set the value of the other field in the related field can succeed.
-          }
-          else
-          {
-            g_warning("Box_Data_List::on_flowtable_field_edited(): key not found for edited related field.");
-          }
-        }
-      }
-
-      //Update the field in the record (the record with this primary key):
-      const Gnome::Gda::Value field_value = m_AddDel.get_value(row, layout_field);
-      //std::cout << "Box_Data_List::on_adddel_user_changed(): field_value = " << field_value.to_string() << std::endl;
-      //const sharedptr<const Field>& field = layout_field->m_field;
-      //const Glib::ustring strFieldName = layout_field->get_name();
-
-      LayoutFieldInRecord field_in_record(layout_field, m_table_name /* parent */, primary_key_field, primary_key_value);
-
-      //Check whether the value meets uniqueness constraints:
-      Gtk::Window* window = get_app_window();
-      if(!check_entered_value_for_uniqueness(m_table_name, row, layout_field, field_value, window))
-      {
-        //Revert to the value in the database:
-        const Gnome::Gda::Value value_old = get_field_value_in_database(field_in_record, window);
-        set_entered_field_data(row, layout_field, value_old);
-
-        return; //The value has been reverted to the value in the database.
-      }
-
-
-      const bool bTest = set_field_value_in_database(field_in_record, row, field_value, false /* don't use current calculations */, window);
-
-      //Glib::ustring strQuery = "UPDATE \"" + table_name + "\"";
-      //strQuery += " SET " +  /* table_name + "." + postgres does not seem to like the table name here */ strFieldName + " = " + field.sql(field_value);
-      //strQuery += " WHERE " + table_name + "." + primary_key_field.get_name() + " = " + primary_key_field.sql(primary_key_value);
-      //bool bTest = query_execute(strQuery);
-      if(!bTest)
-      {
-        //Update failed.
-        fill_from_database(); //Replace with correct values.
-      }
-    }
-#ifdef GLIBMM_EXCEPTIONS_ENABLED
-    catch(const Glib::Exception& ex)
-    {
-      handle_error(ex);
-    }
-    catch(const std::exception& ex)
-    {
-      handle_error(ex);
-    }
-#endif // GLIBMM_EXCEPTIONS_ENABLED
-  }
-  else
-  {
-    //This record probably doesn't exist yet.
-    //Add new record, which will generate the primary key:
-    //Actually, on_adddel_user_added() is usually just called directly in response to the user_added signal.
-    on_adddel_user_added(row, col);
-    
-    const Gnome::Gda::Value primaryKeyValue = get_primary_key_value(row); //TODO_Value
-    if(!(Conversions::value_is_empty(primaryKeyValue))) //If the Add succeeeded:
-    {
-      if(!(layout_field->get_full_field_details()->get_primary_key())) //Don't try to re-set the primary key field, because we just inserted the record with it.
-      {
-        on_adddel_user_changed(row, col); //Change this field in the new record.
-      }
-    }
-    else
-    {
-      //A field value was entered, but the record has not been added yet, because not enough information exists yet.
-       g_warning("Box_Data_List::on_adddel_user_changed(): debug: record not yet added.");
-    }
-  }
-
-}
-
-void Box_Data_List::on_adddel_script_button_clicked(const sharedptr<const LayoutItem_Button>& layout_item, const Gtk::TreeModel::iterator& row)
-{
-  if(layout_item)
-  {
-    const Gnome::Gda::Value primary_key_value = get_primary_key_value(row);
-    execute_button_script(layout_item, primary_key_value);
-
-    // Refill view from database as the script might have changed arbitrary records
-
-#if 0
-    // TODO: This is perhaps a better approach, but
-    // DbTreeModel::refresh_from_database is protected
-    Glib::RefPtr<Gtk::TreeModel> model = m_AddDel.get_model();
-    Glib::RefPtr<DbTreeModel> db_model = Glib::RefPtr<DbTreeModel>::cast_dynamic(model);
-    if(db_model) db_model->refresh_from_database(m_found_set);
-#endif
-
-    // TODO: Calling refresh_data_from_database() causes a crash somewhere
-    // down in GTK+, so it is done in a handler here.
-    // We are currently in a callback from the CellRendererButton_Text cell
-    // renderer which is deleted by a call to refresh_data_from_database().
-    // Probably this causes issues somewhere. 
-    Glib::signal_idle().connect(sigc::bind(sigc::mem_fun(*this, &Box_Data_List::on_script_button_idle), primary_key_value));
-
-    //refresh_data_from_database();
-    //set_primary_key_value_selected(primary_key);
-  }
-}
-
-bool Box_Data_List::on_script_button_idle(const Gnome::Gda::Value& primary_key)
-{
-  refresh_data_from_database();
-  set_primary_key_value_selected(primary_key);
-  return false;
-}
-
-void Box_Data_List::on_details_nav_first()
-{
-  m_AddDel.select_item(m_AddDel.get_model()->children().begin());
-
-  signal_user_requested_details().emit(m_AddDel.get_value_key_selected());
-}
-
-void Box_Data_List::on_details_nav_previous()
-{
-  Gtk::TreeModel::iterator iter = m_AddDel.get_item_selected();
-  if(iter)
-  {
-    //Don't try to select a negative record number.
-    if(!m_AddDel.get_is_first_row(iter))
-    {
-      iter--;
-
-      m_AddDel.select_item(iter);
-      signal_user_requested_details().emit(m_AddDel.get_value_key_selected());
-    }
-  }
-}
-
-void Box_Data_List::on_details_nav_next()
-{
-  Gtk::TreeModel::iterator iter = m_AddDel.get_item_selected();
-  if(iter)
-  {
-    //Don't go past the last record:
-    if( !m_AddDel.get_is_last_row(iter) )
-    {
-      std::cout << "DEBUG: Box_Data_List::on_details_nav_next(): The current row was not the last row." << std::endl;
-
-      iter++;
-      m_AddDel.select_item(iter);
-
-      signal_user_requested_details().emit(m_AddDel.get_value_key_selected());
-    }
-    else
-      std::cout << "DEBUG: Box_Data_List::on_details_nav_next(): Not going past the last row." << std::endl;
-  }
-}
-
-void Box_Data_List::on_details_nav_last()
-{
-  Gtk::TreeModel::iterator iter = m_AddDel.get_last_row();
-  if(iter)
-  {
-    m_AddDel.select_item(iter);
-    signal_user_requested_details().emit(m_AddDel.get_value_key_selected());
-  }
-  
-  //No, don't do this. When would that ever be a good idea? murrayc:
-  //signal_user_requested_details().emit(Gnome::Gda::Value()); //Show a blank record if there are no records.
-}
-
-void Box_Data_List::on_details_record_deleted(const Gnome::Gda::Value& primary_key_value)
-{
-  //Find out which row is affected:
-  Gtk::TreeModel::iterator iter = m_AddDel.get_row(primary_key_value);
-  if(iter)
-  {
-    //Remove the row:
-    Gtk::TreeModel::iterator iterNext = iter;
-    iterNext++;
-
-    m_AddDel.remove_item(iter);
-
-    //Show Details for the next one:
-    if(iterNext != m_AddDel.get_model()->children().end())
-    {
-      //Next record moves up one:
-      on_adddel_user_requested_edit(iterNext);
-    }
-    else
-    {
-      //Just show the last one:
-      on_details_nav_last();
-    }
-  }
-  else
-  {
-    //Just update everything and go the first record.
-    //This shouldn't happen.
-    fill_from_database();
-    on_details_nav_first();
-  }
-}
-
-Gnome::Gda::Value Box_Data_List::get_primary_key_value(const Gtk::TreeModel::iterator& row)
-{
-  return m_AddDel.get_value_key(row);
-}
-
-Gnome::Gda::Value Box_Data_List::get_primary_key_value_selected()
-{
-  return m_AddDel.get_value_key_selected();
-}
-
-Gnome::Gda::Value Box_Data_List::get_primary_key_value_first()
-{
-  //std::cout << "Box_Data_List(): get_primary_key_value_first() records_count = " << m_AddDel.get_count() << std::endl;
-
-  Glib::RefPtr<Gtk::TreeModel> model = m_AddDel.get_model();
-  if(model)
-  {
-    Gtk::TreeModel::iterator iter = model->children().begin();
-    while(iter != model->children().end())
-    {
-      Gnome::Gda::Value value = get_primary_key_value(iter);
-      if(Conversions::value_is_empty(value))
-      {
-       //std::cout << "Box_Data_List(): get_primary_key_value_first() iter val is NULL" << std::endl;
-        ++iter;
-      }
-      else
-      {
-         //std::cout << "Box_Data_List(): get_primary_key_value_first() returning: " << value.to_string() << std::endl;
-        return value;
-      }
-    }
-  }
-
- // std::cout << "Box_Data_List(): get_primary_key_value_first() return NULL" << std::endl;
-  return Gnome::Gda::Value();
-}
-
-Gnome::Gda::Value Box_Data_List::get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const
-{
-  return m_AddDel.get_value_selected(field);
-}
-
-void Box_Data_List::set_entered_field_data(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value)
-{
-  return m_AddDel.set_value_selected(field, value);
-}
-
-void Box_Data_List::set_entered_field_data(const Gtk::TreeModel::iterator& row, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value)
-{
-  return m_AddDel.set_value(row, field, value);
 }
 
-bool Box_Data_List::get_showing_multiple_records() const
+Box_Data_ManyRecords::~Box_Data_ManyRecords()
 {
-  return m_AddDel.get_count() > 1;
 }
 
-void Box_Data_List::create_layout_add_group(const sharedptr<LayoutGroup>& layout_group)
+void Box_Data_ManyRecords::refresh_data_from_database_blank()
 {
-  if(!layout_group)
-    return;
-
-  LayoutGroup::type_list_items child_items = layout_group->get_items();
-  for(LayoutGroup::type_list_items::const_iterator iter = child_items.begin(); iter != child_items.end(); ++iter)
-  {
-    sharedptr<LayoutItem> child_item = *iter;
-
-    sharedptr<LayoutGroup> child_group = sharedptr<LayoutGroup>::cast_dynamic(child_item);
-    if(child_group)
-    {
-      //std::cout << "debug: Start Adding child group." << std::endl;
-      create_layout_add_group(child_group);
-      //std::cout << "debug: End Adding child group." << std::endl;
-    }
-    else
-    {
-      if(m_read_only)
-        child_item->set_editable(false);
-
-      //std::cout << "debug: adding column: " << child_item->get_name() << std::endl;
-
-      sharedptr<LayoutItem_Field> child_field = sharedptr<LayoutItem_Field>::cast_dynamic(child_item);
-      if(child_field)
-      {
-        //Check that the field really exists, to avoid SQL errors.
-        //This could probably only happen if we have failed to rename something everywhere, when the user has renamed something.
-        if(!get_field_exists_in_database(child_field->get_table_used(m_table_name), child_field->get_name()))
-        {
-          std::cerr << "debug: Box_Data_List::create_layout_add_group(): Field does not exist in database: table_name=" << child_field->get_table_used(m_table_name) << ", field_name=" << child_field->get_name() << std::endl;
-          continue;
-        }
-      }
-
-      //Sometimes we reset the column width so that new fields are easily visible:
-      if(m_reset_column_widths)
-        child_item->set_display_width(0);
-
-      m_AddDel.add_column(child_item);
-    }
-  }
+  //Overridden by derived classes.
 }
 
-Document_Glom::type_list_layout_groups Box_Data_List::create_layout_get_layout()
+Document_Glom::type_list_layout_groups Box_Data_ManyRecords::create_layout_get_layout()
 {
-  //Overriden in Box_Data_List_Related:
+  //Overriden in Box_Data_ManyRecords_Related:
   return get_data_layout_groups(m_layout_name); 
 }
 
-void Box_Data_List::create_layout()
-{
-  Box_Data::create_layout(); //Fills m_TableFields.
-
-  const Document_Glom* pDoc = dynamic_cast<const Document_Glom*>(get_document());
-  if(pDoc)
-  {
-    //Field Names:
-    m_AddDel.remove_all_columns();
-    //m_AddDel.set_columns_count(m_Fields.size());
-
-    m_AddDel.set_table_name(m_table_name);
-
-
-    sharedptr<Field> field_primary_key = get_field_primary_key_for_table(m_table_name);
-    if(!field_primary_key)
-    {
-      //g_warning("%s: primary key not found.", __FUNCTION__);
-    }
-    else
-    {
-      m_AddDel.set_key_field(field_primary_key);
- 
-      //This map of layout groups will also contain the field information from the database:
-      Document_Glom::type_list_layout_groups layout_groups = create_layout_get_layout();
-
-      //int debug_count = 0;
-      for(Document_Glom::type_list_layout_groups::const_iterator iter = layout_groups.begin(); iter != layout_groups.end(); ++iter)
-      {
-        //std::cout << "Box_Data_List::create_layout() group number=" << debug_count;
-        //debug_count++;
-        //iter->second->debug();
-
-        create_layout_add_group(*iter);
-      }
-    }
-
-
-    m_FieldsShown = get_fields_to_show();
-
-    //Add extra possibly-non-visible columns that we need:
-    //TODO: Only add it if it is not already there.
-    if(field_primary_key)
-    {
-      sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
-      layout_item->set_hidden();
-      layout_item->set_full_field_details(m_AddDel.get_key_field());
-      m_FieldsShown.push_back(layout_item);
-
-      m_AddDel.add_column(layout_item);
-    }
-
-    m_AddDel.set_found_set(m_found_set);
-
-    //Column-creation happens in fill_database() instead:
-    //otherwise the treeview will be filled twice.
-    //m_AddDel.set_columns_ready();
-  }
-
-}
-
-void Box_Data_List::on_record_deleted(const Gnome::Gda::Value& /* primary_key_value */)
-{
-   //Overridden by Box_Data_List_Related.
-}
-
-void Box_Data_List::on_record_added(const Gnome::Gda::Value& /* strPrimaryKey */, const Gtk::TreeModel::iterator& /* row */)
-{
-  //Overridden by Box_Data_List_Related.
-  //m_AddDel.add_item(strPrimaryKey); //Add blank row.
-}
-
-Box_Data_List::type_signal_user_requested_details Box_Data_List::signal_user_requested_details()
+Box_Data_ManyRecords::type_signal_user_requested_details Box_Data_ManyRecords::signal_user_requested_details()
 {
   return m_signal_user_requested_details;
 }
 
-sharedptr<Field> Box_Data_List::get_field_primary_key() const
-{
-  return m_AddDel.get_key_field();
-}
-
-bool Box_Data_List::get_field_primary_key_index(guint& field_column) const
-{
-  return Box_Data::get_field_primary_key_index_for_fields(m_FieldsShown, field_column);
-}
-
-void Box_Data_List::print_layout()
+void Box_Data_ManyRecords::print_layout()
 {
   const Privileges table_privs = Privs::get_current_privs(m_table_name);
 
@@ -862,73 +90,14 @@
   }
 }
 
-void Box_Data_List::print_layout_group(xmlpp::Element* /* node_parent */, const sharedptr<const LayoutGroup>& /* group */)
-{
-}
-
-void Box_Data_List::set_read_only(bool read_only)
-{
-  //This is useful when showing find results for the user to select one, without changing them.
-  m_read_only = read_only;
-  m_AddDel.set_allow_add(!read_only);
-  m_AddDel.set_allow_delete(!read_only);
-}
-
-void Box_Data_List::set_open_button_title(const Glib::ustring& title)
+void Box_Data_ManyRecords::print_layout_group(xmlpp::Element* /* node_parent */, const sharedptr<const LayoutGroup>& /* group */)
 {
-  m_AddDel.set_open_button_title(title);
-}
-
-void Box_Data_List::set_primary_key_value_selected(const Gnome::Gda::Value& primary_key_value)
-{
-  Gtk::TreeModel::iterator iter = m_AddDel.get_row(primary_key_value);
-  if(iter)
-  {
-    m_AddDel.select_item(iter);
-  }
-}
-
-void Box_Data_List::get_record_counts(gulong& total, gulong& found) const
-{
-  //Initialize output parameters:
-  total = 0;
-  found = 0;
-
-  Glib::RefPtr<Gtk::TreeModel> refModel = m_AddDel.get_model();
-  Glib::RefPtr<DbTreeModel> refModelDerived = Glib::RefPtr<DbTreeModel>::cast_dynamic(refModel);
-  
-  if(refModelDerived)
-    refModelDerived->get_record_counts(total, found);
-}
-
-#ifndef GLOM_ENABLE_CLIENT_ONLY
-//overridden, so we can change the column widths, so they are all visible:
-void Box_Data_List::on_dialog_layout_hide()
-{
-  //Tell create_layout() to reset the display_width for each layout item:
-  m_reset_column_widths = true;
-  Box_Data::on_dialog_layout_hide();
-  m_reset_column_widths = false;
-}
-
-Dialog_Layout* Box_Data_List::create_layout_dialog() const
-{
-  Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "window_data_layout"); //TODO: Use a generic layout dialog?
-  if(refXml)
-  {
-    Dialog_Layout_List* dialog = 0;
-    refXml->get_widget_derived("window_data_layout", dialog);
-    return dialog;
-  }
-
-  return NULL;
 }
 
-void Box_Data_List::prepare_layout_dialog(Dialog_Layout* dialog)
+void Box_Data_ManyRecords::set_primary_key_value_selected(const Gnome::Gda::Value& /* primary_key_value */)
 {
-  dialog->set_document(m_layout_name, get_document(), m_table_name, m_FieldsShown); //TODO: Use m_TableFields?
+  //Overridden in derived classes.
 }
-#endif // !GLOM_ENABLE_CLIENT_ONLY
 
 } //namespace Glom
 

Copied: trunk/glom/mode_data/box_data_manyrecords.h (from r1587, /trunk/glom/mode_data/box_data_list.h)
==============================================================================
--- /trunk/glom/mode_data/box_data_list.h	(original)
+++ trunk/glom/mode_data/box_data_manyrecords.h	Tue Apr 29 14:32:35 2008
@@ -18,8 +18,8 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef BOX_DATA_LIST_H
-#define BOX_DATA_LIST_H
+#ifndef BOX_DATA_MANY_RECORDS_H
+#define BOX_DATA_MANY_RECORDS_H
 
 #include "config.h" // GLOM_ENABLE_CLIENT_ONLY
 
@@ -29,25 +29,14 @@
 namespace Glom
 {
 
-class Box_Data_List : public Box_Data
+class Box_Data_ManyRecords : public Box_Data
 {
 public: 
-  Box_Data_List();
-  virtual ~Box_Data_List();
+  Box_Data_ManyRecords();
+  virtual ~Box_Data_ManyRecords();
 
   void refresh_data_from_database_blank();
 
-  virtual Gnome::Gda::Value get_primary_key_value(const Gtk::TreeModel::iterator& row);
-  virtual Gnome::Gda::Value get_primary_key_value_selected();
-  Gnome::Gda::Value get_primary_key_value_first();
-
-  ///This allows Box_Data::record_new() to set the generated/entered primary key value, needed by Box_Data_List:
-  virtual void set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value);
-
-  virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
-  virtual void set_entered_field_data(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
-  virtual void set_entered_field_data(const Gtk::TreeModel::iterator& row, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
-
   bool get_showing_multiple_records() const;
 
   void set_read_only(bool read_only = true);
@@ -56,68 +45,26 @@
   void set_open_button_title(const Glib::ustring& title);
 
   ///Highlight and scroll to the specified record, with primary key value @primary_key_value.
-  void set_primary_key_value_selected(const Gnome::Gda::Value& primary_key_value);
+  virtual void set_primary_key_value_selected(const Gnome::Gda::Value& primary_key_value);
 
   //Primary Key value:
   typedef sigc::signal<void, const Gnome::Gda::Value&> type_signal_user_requested_details;
   type_signal_user_requested_details signal_user_requested_details();
 
-  //Signal Handlers:
-  virtual void on_details_nav_first();
-  virtual void on_details_nav_previous();
-  virtual void on_details_nav_next();
-  virtual void on_details_nav_last();
-  virtual void on_details_record_deleted(const Gnome::Gda::Value& primary_key_value);
-
   void get_record_counts(gulong& total, gulong& found) const;
 
-  #ifndef GLOM_ENABLE_CLIENT_ONLY
-  virtual void on_dialog_layout_hide(); //override
-  #endif //GLOM_ENABLE_CLIENT_ONLY
 
 protected:
-  virtual void create_layout(); //override
-  virtual Document_Glom::type_list_layout_groups create_layout_get_layout(); //overriden in Box_Data_List_Related.
+  virtual Document_Glom::type_list_layout_groups create_layout_get_layout(); //overriden in Box_Data_ManyRecords_Related.
   void create_layout_add_group(const sharedptr<LayoutGroup>& layout_group);
-  virtual bool fill_from_database(); //override.
-  virtual void enable_buttons();
-
-  virtual bool get_field_primary_key_index(guint& field_column) const; //TODO: visible 
-  virtual sharedptr<Field> get_field_primary_key() const;
-
-  //Signal handlers:
-  virtual void on_adddel_user_requested_add();
-  virtual void on_adddel_user_requested_edit(const Gtk::TreeModel::iterator& row);
-  virtual void on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
-  virtual void on_adddel_user_added(const Gtk::TreeModel::iterator& row, guint col_with_first_value);
-  virtual void on_adddel_user_reordered_columns();
-
-#ifndef GLOM_ENABLE_CLIENT_ONLY
-  virtual void on_adddel_user_requested_layout();
-#endif // !GLOM_ENABLE_CLIENT_ONLY
-
-  virtual void on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col);
-  virtual void on_adddel_script_button_clicked(const sharedptr<const LayoutItem_Button>& layout_item, const Gtk::TreeModel::iterator& row);
-  virtual bool on_script_button_idle(const Gnome::Gda::Value& primary_key_value);
-
-  virtual void on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row); //Not a signal handler. To be overridden.
-  virtual void on_record_deleted(const Gnome::Gda::Value& primary_key_value);
 
   virtual void print_layout();
   virtual void print_layout_group(xmlpp::Element* node_parent, const sharedptr<const LayoutGroup>& group);
 
-#ifndef GLOM_ENABLE_CLIENT_ONLY
-  virtual Dialog_Layout* create_layout_dialog() const; // override.
-  virtual void prepare_layout_dialog(Dialog_Layout* dialog); // override.
-#endif // !GLOM_ENABLE_CLIENT_ONLY
-
-  //Member widgers:
-  mutable DbAddDel_WithButtons m_AddDel; //mutable because its get_ methods aren't const.
-
+  //TODO: remove?
   bool m_has_one_or_more_records;
   bool m_read_only;
 
-  bool m_reset_column_widths; //create_layout() sets these to 0 when this is set.
 
   type_signal_user_requested_details m_signal_user_requested_details;
 };

Modified: trunk/glom/mode_data/box_data_portal.cc
==============================================================================
--- trunk/glom/mode_data/box_data_portal.cc	(original)
+++ trunk/glom/mode_data/box_data_portal.cc	Tue Apr 29 14:32:35 2008
@@ -56,7 +56,7 @@
   m_portal = glom_sharedptr_clone(portal);
 
   LayoutWidgetBase::m_table_name = m_portal->get_table_used(Glib::ustring() /* parent table_name, not used. */); 
-  Box_DB_Table::m_table_name = LayoutWidgetBase::m_table_name;
+  Base_DB_Table::m_table_name = LayoutWidgetBase::m_table_name;
 
   const Glib::ustring relationship_title = m_portal->get_title_used(Glib::ustring() /* parent title - not relevant */);
   
@@ -77,11 +77,9 @@
 
   m_key_field = get_fields_for_table_one_field(LayoutWidgetBase::m_table_name, m_portal->get_to_field_used());
 
-  enable_buttons();
-
   FoundSet found_set;
   found_set.m_table_name = LayoutWidgetBase::m_table_name;
-  return Box_Data_List::init_db_details(found_set); //Calls create_layout() and fill_from_database().
+  return Box_Data::init_db_details(found_set); //Calls create_layout() and fill_from_database().
 }
 
 bool Box_Data_Portal::refresh_data_from_database_with_foreign_key(const Gnome::Gda::Value& foreign_key_value)
@@ -129,8 +127,8 @@
       found_set.m_where_clause = "\"" + where_clause_to_table_name + "\".\"" + relationship->get_to_field() + "\" = " + where_clause_to_key_field->sql(m_key_value);
 
 
-      //g_warning("refresh_data_from_database(): where_clause=%s", where_clause.c_str());
-      return Box_Data_List::refresh_data_from_database_with_where_clause(found_set);
+      //g_warning("refresh_data_from_database_with_foreign_key(): where_clause=%s", where_clause.c_str());
+      return Box_Data::refresh_data_from_database_with_where_clause(found_set);
     }
     else
     {
@@ -144,7 +142,7 @@
     //If there is no to field then this relationship specifies all records in the table.
     FoundSet found_set = m_found_set;
     found_set.m_where_clause = Glib::ustring();
-    return Box_Data_List::refresh_data_from_database_with_where_clause(found_set);
+    return Box_Data::refresh_data_from_database_with_where_clause(found_set);
   }
 }
 
@@ -158,11 +156,18 @@
   return m_key_field;
 }
 
-//void Box_Data_Portal::on_record_deleted(const Gnome::Gda::Value& /* primary_key_value */)
-//{
-//  //Allow the parent record (Details view) to recalculate aggregations:
-//  signal_record_changed().emit(m_portal->get_relationship_name());
-//}
+//TODO: refactor: Remove this because it is never called?
+void Box_Data_Portal::on_record_deleted(const Gnome::Gda::Value& /* primary_key_value */)
+{
+  //Allow the parent record (Details view) to recalculate aggregations:
+  signal_portal_record_changed().emit(m_portal->get_relationship_name());
+}
+
+void Box_Data_Portal::on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row)
+{
+  //Allow the parent record (Details view) to recalculate aggregations:
+  signal_portal_record_changed().emit(m_portal->get_relationship_name());
+}
 
 Box_Data_Portal::type_vecLayoutFields Box_Data_Portal::get_fields_to_show() const
 {
@@ -195,11 +200,6 @@
   return type_vecLayoutFields();
 }
 
-Box_Data_Portal::type_signal_record_changed Box_Data_Portal::signal_record_changed()
-{
-  return m_signal_record_changed;
-}
-
 #ifndef GLOM_ENABLE_CLIENT_ONLY
 void Box_Data_Portal::on_dialog_layout_hide()
 {
@@ -352,4 +352,15 @@
   return result;
 }
 
+sharedptr<Field> Box_Data_Portal::get_field_primary_key() const
+{
+  return m_key_field;
+}
+
+Box_Data_Portal::type_signal_portal_record_changed Box_Data_Portal::signal_portal_record_changed()
+{
+  return m_signal_portal_record_changed;
+}
+
+
 } //namespace Glom

Modified: trunk/glom/mode_data/box_data_portal.h
==============================================================================
--- trunk/glom/mode_data/box_data_portal.h	(original)
+++ trunk/glom/mode_data/box_data_portal.h	Tue Apr 29 14:32:35 2008
@@ -23,7 +23,7 @@
 
 #include "config.h" // GLOM_ENABLE_CLIENT_ONLY
 
-#include "box_data_list.h"
+#include "box_data_manyrecords.h"
 #include "../utility_widgets/layoutwidgetbase.h"
 
 namespace Glom
@@ -32,8 +32,8 @@
 /** This is a base class for data widgets that should show multiple related records.
  */
 class Box_Data_Portal : 
-  public Box_Data_List,
-  public LayoutWidgetBase
+  public Box_Data_ManyRecords,
+  public LayoutWidgetBase     
 {
 public: 
   Box_Data_Portal();
@@ -55,20 +55,27 @@
 
   sigc::signal<void, Gnome::Gda::Value> signal_record_added;
 
+  /** Tell the parent widget that something has changed in one of the related records,
+   * or a record was added or deleted.
+   *
+   * @param relationship_name, if any.
+   */
+  typedef sigc::signal<void, const Glib::ustring&> type_signal_portal_record_changed;
+  type_signal_portal_record_changed signal_portal_record_changed();
+    
   bool get_has_suitable_record_to_view_details() const;
   void get_suitable_table_to_view_details(Glib::ustring& table_name, sharedptr<const UsesRelationship>& relationship) const;
   void get_suitable_record_to_view_details(const Gnome::Gda::Value& primary_key_value, Glib::ustring& table_name, Gnome::Gda::Value& table_primary_key_value) const;
 
-  ///@param relationship_name
-  typedef sigc::signal<void, const Glib::ustring&> type_signal_record_changed;
-  type_signal_record_changed signal_record_changed();
-
 protected:
-  //virtual bool fill_from_database(); //Override.
   virtual type_vecLayoutFields get_fields_to_show() const; //override
-
-  //virtual void on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row); //Override. Not a signal handler.
-  //virtual void on_record_deleted(const Gnome::Gda::Value& primary_key_value); //override.
+    
+  //Implementations of pure virtual methods from Base_DB_Table_Data:
+  virtual sharedptr<Field> get_field_primary_key() const;
+
+  //Overrides of virtual methods from Base_Db_Table_Data: 
+  virtual void on_record_added(const Gnome::Gda::Value& primary_key_value, const Gtk::TreeModel::iterator& row); //Override. Not a signal handler.
+  virtual void on_record_deleted(const Gnome::Gda::Value& primary_key_value); //override.
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
   virtual void on_dialog_layout_hide(); //override.
@@ -84,8 +91,8 @@
   sharedptr<LayoutItem_Portal> m_portal;
   sharedptr<Field> m_key_field;
   Gnome::Gda::Value m_key_value;
-
-  type_signal_record_changed m_signal_record_changed;
+    
+  type_signal_portal_record_changed m_signal_portal_record_changed;
 };
 
 } //namespace Glom

Modified: trunk/glom/mode_data/dialog_choose_field.h
==============================================================================
--- trunk/glom/mode_data/dialog_choose_field.h	(original)
+++ trunk/glom/mode_data/dialog_choose_field.h	Tue Apr 29 14:32:35 2008
@@ -24,7 +24,7 @@
 #include <gtkmm.h>
 #include "../utility_widgets/dialog_properties.h"
 #include <glom/libglom/document/document_glom.h>
-#include "../box_db.h"
+#include "../box_withbuttons.h"
 #include "../utility_widgets/combo_textglade.h"
 #include "../combobox_relationship.h"
 #include "../utility_widgets/comboentry_currency.h"

Modified: trunk/glom/mode_data/dialog_choose_relationship.h
==============================================================================
--- trunk/glom/mode_data/dialog_choose_relationship.h	(original)
+++ trunk/glom/mode_data/dialog_choose_relationship.h	Tue Apr 29 14:32:35 2008
@@ -24,7 +24,7 @@
 #include <gtkmm/dialog.h>
 #include "../utility_widgets/dialog_properties.h"
 #include <glom/libglom/document/document_glom.h>
-#include "../box_db.h"
+#include "../box_withbuttons.h"
 
 namespace Glom
 {

Modified: trunk/glom/mode_data/dialog_layout.h
==============================================================================
--- trunk/glom/mode_data/dialog_layout.h	(original)
+++ trunk/glom/mode_data/dialog_layout.h	Tue Apr 29 14:32:35 2008
@@ -24,7 +24,7 @@
 #include <gtkmm/dialog.h>
 #include "../utility_widgets/dialog_properties.h"
 #include <glom/libglom/document/document_glom.h>
-#include "../box_db.h"
+#include "../box_withbuttons.h"
 
 namespace Glom
 {

Modified: trunk/glom/mode_data/flowtablewithfields.cc
==============================================================================
--- trunk/glom/mode_data/flowtablewithfields.cc	(original)
+++ trunk/glom/mode_data/flowtablewithfields.cc	Tue Apr 29 14:32:35 2008
@@ -36,6 +36,7 @@
 #include <bakery/App/App_Gtk.h> //For util_bold_message().
 #include <glibmm/i18n.h>
 #include <glom/libglom/data_structure/layout/layoutitem_placeholder.h>
+#include <glom/signal_reemitter.h>
 
 namespace Glom
 {
@@ -261,7 +262,8 @@
       m_portals.push_back(portal_box);
 
       //Connect signals:
-      portal_box->signal_record_changed().connect( sigc::mem_fun(*this, &FlowTableWithFields::on_portal_record_changed) );
+      //Just reemit this object's signal when receiving the same signal from the portal:
+      signal_connect_for_reemit_1arg(portal_box->signal_portal_record_changed(), signal_related_record_changed());
 
       portal_box->signal_user_requested_details().connect( sigc::bind( sigc::mem_fun(*this, &FlowTableWithFields::on_portal_user_requested_details), portal_box));
 
@@ -294,7 +296,8 @@
       m_portals.push_back(portal_box);
 
       //Connect signals:
-      portal_box->signal_record_changed().connect( sigc::mem_fun(*this, &FlowTableWithFields::on_portal_record_changed) );
+      //Just reemit this object's signal when receiving the same signal from the portal:
+      signal_connect_for_reemit_1arg(portal_box->signal_portal_record_changed(), signal_related_record_changed());    
 
       portal_box->signal_user_requested_details().connect( sigc::bind( sigc::mem_fun(*this, &FlowTableWithFields::on_portal_user_requested_details), portal_box));
 
@@ -1152,11 +1155,6 @@
 }
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
-void FlowTableWithFields::on_portal_record_changed(const Glib::ustring& relationship_name)
-{
-  signal_related_record_changed().emit(relationship_name);
-}
-
 void FlowTableWithFields::on_flowtable_related_record_changed(const Glib::ustring& relationship_name)
 {
   //TODO_DoublyRelated
@@ -1440,7 +1438,7 @@
 
 bool FlowTableWithFields::on_button_press_event(GdkEventButton *event)
 {
-	App_Glom* pApp = App_Glom::get_application();
+  App_Glom* pApp = App_Glom::get_application();
   if(pApp && pApp->get_userlevel() == AppState::USERLEVEL_DEVELOPER)
   {
     GdkModifierType mods;

Modified: trunk/glom/mode_data/flowtablewithfields.h
==============================================================================
--- trunk/glom/mode_data/flowtablewithfields.h	(original)
+++ trunk/glom/mode_data/flowtablewithfields.h	Tue Apr 29 14:32:35 2008
@@ -152,7 +152,6 @@
   void on_entry_open_details_requested(const Gnome::Gda::Value& value, sharedptr<const LayoutItem_Field> field);
   void on_flowtable_entry_edited(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
   void on_flowtable_entry_open_details_requested(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value); 
-  void on_portal_record_changed(const Glib::ustring& relationship_name);
   void on_flowtable_related_record_changed(const Glib::ustring& relationship_name);
   void on_flowtable_requested_related_details(const Glib::ustring& table_name, Gnome::Gda::Value primary_key_value);
 

Modified: trunk/glom/mode_design/box_db_table_relationships.cc
==============================================================================
--- trunk/glom/mode_design/box_db_table_relationships.cc	(original)
+++ trunk/glom/mode_design/box_db_table_relationships.cc	Tue Apr 29 14:32:35 2008
@@ -124,8 +124,6 @@
     }
   }
 
-  fill_end();
-
   return result;
 }
 

Modified: trunk/glom/mode_design/box_db_table_relationships.h
==============================================================================
--- trunk/glom/mode_design/box_db_table_relationships.h	(original)
+++ trunk/glom/mode_design/box_db_table_relationships.h	Tue Apr 29 14:32:35 2008
@@ -22,6 +22,7 @@
 #define BOX_DB_TABLE_RELATIONSHIPS_H
 
 #include "../box_db_table.h"
+#include <glom/utility_widgets/adddel/adddel_withbuttons.h>
 
 namespace Glom
 {
@@ -41,10 +42,10 @@
   virtual bool fill_from_database();
 
   //Signal handlers:
-  virtual void on_adddel_user_activated(const Gtk::TreeModel::iterator& row, guint col);
-  virtual void on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col);
-  virtual void on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
-  virtual void on_adddel_user_added(const Gtk::TreeModel::iterator& row);
+  void on_adddel_user_activated(const Gtk::TreeModel::iterator& row, guint col);
+  void on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col);
+  void on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
+  void on_adddel_user_added(const Gtk::TreeModel::iterator& row);
 
   guint m_colName, m_colTitle, m_colFromField, m_colToTable, m_colToField, m_colAllowEdit, m_colAutoCreate;
 

Modified: trunk/glom/mode_design/fields/box_db_table_definition.cc
==============================================================================
--- trunk/glom/mode_design/fields/box_db_table_definition.cc	(original)
+++ trunk/glom/mode_design/fields/box_db_table_definition.cc	Tue Apr 29 14:32:35 2008
@@ -157,8 +157,6 @@
     result = false;
   }
 
-  fill_end();
-
   return result;
 }
 

Modified: trunk/glom/mode_design/print_layouts/box_print_layouts.cc
==============================================================================
--- trunk/glom/mode_design/print_layouts/box_print_layouts.cc	(original)
+++ trunk/glom/mode_design/print_layouts/box_print_layouts.cc	Tue Apr 29 14:32:35 2008
@@ -69,7 +69,7 @@
 {
   Bakery::BusyCursor busy_cursor(get_app_window());
 
-  bool result = Box_DB::fill_from_database();
+  bool result = Base_DB::fill_from_database();
 
   //Enable/Disable extra widgets:
   bool developer_mode = (get_userlevel() == AppState::USERLEVEL_DEVELOPER);
@@ -117,8 +117,6 @@
 
   //TODO:
 
-  fill_end();
-
   m_AddDel.set_allow_add(developer_mode);
   m_AddDel.set_allow_delete(developer_mode);
 

Modified: trunk/glom/mode_design/print_layouts/box_print_layouts.h
==============================================================================
--- trunk/glom/mode_design/print_layouts/box_print_layouts.h	(original)
+++ trunk/glom/mode_design/print_layouts/box_print_layouts.h	Tue Apr 29 14:32:35 2008
@@ -23,6 +23,7 @@
 
 #include <glom/box_db_table.h>
 #include <glom/libglom/data_structure/print_layout.h>
+#include <glom/utility_widgets/adddel/adddel_withbuttons.h>
 
 namespace Glom
 {
@@ -41,10 +42,10 @@
   virtual void save_to_document();
 
   //Signal handlers:
-  virtual void on_adddel_Add(const Gtk::TreeModel::iterator& row);
-  virtual void on_adddel_Delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
-  virtual void on_adddel_Edit(const Gtk::TreeModel::iterator& row);
-  virtual void on_adddel_changed(const Gtk::TreeModel::iterator& row, guint column);
+  void on_adddel_Add(const Gtk::TreeModel::iterator& row);
+  void on_adddel_Delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
+  void on_adddel_Edit(const Gtk::TreeModel::iterator& row);
+  void on_adddel_changed(const Gtk::TreeModel::iterator& row, guint column);
 
   virtual void on_userlevel_changed(AppState::userlevels userlevel);
 

Modified: trunk/glom/mode_find/box_data_details_find.cc
==============================================================================
--- trunk/glom/mode_find/box_data_details_find.cc	(original)
+++ trunk/glom/mode_find/box_data_details_find.cc	Tue Apr 29 14:32:35 2008
@@ -52,7 +52,7 @@
 {
   Bakery::BusyCursor busy_cursor(get_app_window());
 
-  const bool result = Box_DB_Table::fill_from_database();
+  const bool result = Base_DB_Table_Data::fill_from_database();
   if(!result)
     return result;
 

Modified: trunk/glom/mode_find/box_data_list_find.cc
==============================================================================
--- trunk/glom/mode_find/box_data_list_find.cc	(original)
+++ trunk/glom/mode_find/box_data_list_find.cc	Tue Apr 29 14:32:35 2008
@@ -34,6 +34,9 @@
 
   g_object_set(m_Button_Find.gobj(), "can-default", TRUE, (gpointer)NULL); //TODO: Make this a real method in gtkmm?
 
+  //Prevent the widget from trying to add or change records:
+  m_AddDel.set_find_mode();
+
   show_all_children();
 }
 
@@ -50,7 +53,7 @@
 {
   Bakery::BusyCursor busy_cursor(get_app_window());
 
-  const bool result = Box_DB_Table::fill_from_database();
+  const bool result = Base_DB_Table_Data::fill_from_database();
   if(!result)
     return false;
 
@@ -64,27 +67,12 @@
   return result;
 }
 
-void Box_Data_List_Find::on_adddel_user_changed(const Gtk::TreeModel::iterator& /* row */, guint /* col */)
-{
-  //Just block the implementation in the base class.
-}
-
 
 Gtk::Widget* Box_Data_List_Find::get_default_button() //override
 {
   return &m_Button_Find;
 }
 
-void Box_Data_List_Find::on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& /* rowStart */, const Gtk::TreeModel::iterator&  /* rowEnd TODO */)
-{
-  //Ignore this, instead of updating the database, as the base class does.
-}
-
-void Box_Data_List_Find::on_adddel_user_added(const Gtk::TreeModel::iterator& /* row */, guint /* col_with_first_value */)
-{
-  //Ignore this, instead of updating the database, as the base class does.
-}
-
 bool Box_Data_List_Find::init_db_details(const Glib::ustring& table_name)
 {
   FoundSet found_set;

Modified: trunk/glom/mode_find/box_data_list_find.h
==============================================================================
--- trunk/glom/mode_find/box_data_list_find.h	(original)
+++ trunk/glom/mode_find/box_data_list_find.h	Tue Apr 29 14:32:35 2008
@@ -41,10 +41,6 @@
   virtual bool fill_from_database(); //override.
   virtual void create_layout();
 
-  virtual void on_adddel_user_changed(const Gtk::TreeModel::iterator& row, guint col); //override
-  virtual void on_adddel_user_requested_delete(const Gtk::TreeModel::iterator& /* rowStart */, const Gtk::TreeModel::iterator&  /* rowEnd TODO */);
-  virtual void on_adddel_user_added(const Gtk::TreeModel::iterator& /* row */, guint /* col_with_first_value */);
-
   //Member widgets:
   Gtk::HBox m_HBox;
 };

Modified: trunk/glom/navigation/box_tables.cc
==============================================================================
--- trunk/glom/navigation/box_tables.cc	(original)
+++ trunk/glom/navigation/box_tables.cc	Tue Apr 29 14:32:35 2008
@@ -20,13 +20,14 @@
 
 #include "box_tables.h"
 #include <bakery/App/App_Gtk.h> //For util_bold_message().
+#include <glom/application.h>
 #include <glibmm/i18n.h>
 
 namespace Glom
 {
 
 Box_Tables::Box_Tables(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade)
-: Box_DB(cobject),
+: Box_WithButtons(cobject, refGlade),
   m_pLabelFrameTitle(0),
   m_pCheckButtonShowHidden(0),
   m_colTableName(0),
@@ -87,9 +88,9 @@
 
 bool Box_Tables::fill_from_database()
 {
-  Bakery::BusyCursor busy_cursor(get_app_window());
+  Bakery::BusyCursor busy_cursor(App_Glom::get_application());
 
-  bool result = Box_DB::fill_from_database();
+  bool result = Base_DB::fill_from_database();
 
   //Enable/Disable extra widgets:
   const bool developer_mode = (get_userlevel() == AppState::USERLEVEL_DEVELOPER);
@@ -139,10 +140,10 @@
 
   //Get the list of tables in the database, from the server:
 #ifdef GLIBMM_EXCEPTIONS_ENABLED
-  sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window());
+  sharedptr<SharedConnection> sharedconnection = connect_to_server(App_Glom::get_application());
 #else
   std::auto_ptr<ExceptionConnection> error;
-  sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window(), error);
+  sharedptr<SharedConnection> sharedconnection = connect_to_server(App_Glom::get_application(), error);
   // Ignore error, sharedconnection presence is checked below
 #endif
   if(sharedconnection)
@@ -202,8 +203,6 @@
     }
   }
 
-  fill_end();
-
   m_AddDel.set_allow_add(developer_mode);
   m_AddDel.set_allow_delete(developer_mode);
 
@@ -227,7 +226,7 @@
     //Ask the user if they want us to try to cope with this:
     Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Table Already Exists")), true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL);
     dialog.set_secondary_text(_("This table already exists on the database server, though it is not mentioned in the .glom file. This should not happen. Would you like Glom to attempt to use the existing table?"));
-    dialog.set_transient_for(*get_app_window());
+    dialog.set_transient_for(*App_Glom::get_application());
 
     const int response = dialog.run();
     dialog.hide();
@@ -287,7 +286,7 @@
         {
            //TODO: Do not show tables that are not in the document.
            Gtk::MessageDialog dialog(_("You cannot delete this table, because there is no information about this table in the document."));
-           dialog.set_transient_for(*get_app_window());
+           dialog.set_transient_for(*App_Glom::get_application());
            dialog.run();
         }
         else
@@ -296,13 +295,13 @@
           Glib::ustring strMsg = _("Are you sure that you want to delete this table?\nTable name: ") + table_name;
           Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Delete Table")), true);
           dialog.set_secondary_text(strMsg);
-          dialog.set_transient_for(*get_app_window());
+          dialog.set_transient_for(*App_Glom::get_application());
           int iButtonClicked = dialog.run();
 
           //Delete the table:
           if(iButtonClicked == Gtk::RESPONSE_OK)
           {
-            query_execute( "DROP TABLE \"" + table_name + "\"", get_app_window());
+            query_execute( "DROP TABLE \"" + table_name + "\"", App_Glom::get_application());
             get_document()->remove_table(table_name); //Forget about it in the document too.
             something_changed = true;
           }
@@ -332,7 +331,7 @@
     {
        Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Unknown Table")), true);
        dialog.set_secondary_text(_("You cannot open this table, because there is no information about this table in the document."));
-       dialog.set_transient_for(*get_app_window());
+       dialog.set_transient_for(*App_Glom::get_application());
        dialog.run();
     }
     else
@@ -432,7 +431,7 @@
         //Rename the table:
         if(iButtonClicked == Gtk::RESPONSE_OK)
         {
-          const bool test = query_execute( "ALTER TABLE \"" + table_name + "\" RENAME TO \"" + table_name_new + "\"", get_app_window());
+          const bool test = query_execute( "ALTER TABLE \"" + table_name + "\" RENAME TO \"" + table_name_new + "\"", App_Glom::get_application());
           if(test)
           {
             //Change the AddDel item's key:

Modified: trunk/glom/navigation/box_tables.h
==============================================================================
--- trunk/glom/navigation/box_tables.h	(original)
+++ trunk/glom/navigation/box_tables.h	Tue Apr 29 14:32:35 2008
@@ -21,12 +21,21 @@
 #ifndef BOX_TABLES_H
 #define BOX_TABLES_H
 
-#include "../box_db.h"
+#include <glom/box_withbuttons.h>
+#include <glom/base_db.h>
+#include <glom/utility_widgets/adddel/adddel_withbuttons.h>
+#include <libglademm.h>
 
 namespace Glom
 {
 
-class Box_Tables : public Box_DB
+/** This widget offers a list of tables in the database,
+  * allowing the user to select a table,
+  * or add or delete a table.
+  */
+class Box_Tables 
+: public Box_WithButtons,
+  public Base_DB
 {
 public:
   Box_Tables(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade);
@@ -40,15 +49,15 @@
   virtual void save_to_document();
 
   //Signal handlers:
-  virtual void on_adddel_Add(const Gtk::TreeModel::iterator& row);
-  virtual void on_adddel_Delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
-  virtual void on_adddel_Edit(const Gtk::TreeModel::iterator& row);
-  virtual void on_adddel_changed(const Gtk::TreeModel::iterator& row, guint column);
+  void on_adddel_Add(const Gtk::TreeModel::iterator& row);
+  void on_adddel_Delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator& rowEnd);
+  void on_adddel_Edit(const Gtk::TreeModel::iterator& row);
+  void on_adddel_changed(const Gtk::TreeModel::iterator& row, guint column);
 
-  virtual void on_show_hidden_toggled();
+  void on_show_hidden_toggled();
 
   virtual void on_userlevel_changed(AppState::userlevels userlevel);
-
+      
   Gtk::Label* m_pLabelFrameTitle;
   Gtk::CheckButton* m_pCheckButtonShowHidden;
   guint m_colTableName;

Modified: trunk/glom/notebook_glom.cc
==============================================================================
--- trunk/glom/notebook_glom.cc	(original)
+++ trunk/glom/notebook_glom.cc	Tue Apr 29 14:32:35 2008
@@ -74,7 +74,7 @@
   Gtk::Widget* pChild = get_nth_page(uiPageNumber);
   if(pChild)
   {
-    Box_DB* pBox = dynamic_cast<Box_DB*>(pChild);
+    Box_WithButtons* pBox = dynamic_cast<Box_WithButtons*>(pChild);
     if(pBox)
     {
       //pBox->load_from_document();
@@ -107,7 +107,7 @@
     Gtk::Widget* pChild  = get_nth_page(uiPageNumber);
     if(pChild)
     {
-      Box_DB* pBox = dynamic_cast<Box_DB*>(pChild);
+      Base_DB* pBox = dynamic_cast<Base_DB*>(pChild);
       if(pBox)
       {
         pBox->save_to_document();
@@ -129,7 +129,7 @@
   Gtk::Widget* pChild  = get_nth_page(iPageCurrent);
   if(pChild)
   {
-    Box_DB* pBox = dynamic_cast<Box_DB*>(pChild);
+    Box_WithButtons* pBox = dynamic_cast<Box_WithButtons*>(pChild);
     if(pBox)
       pBox->show_hint();
   }

Modified: trunk/glom/notebook_glom.h
==============================================================================
--- trunk/glom/notebook_glom.h	(original)
+++ trunk/glom/notebook_glom.h	Tue Apr 29 14:32:35 2008
@@ -21,7 +21,7 @@
 #ifndef NOTEBOOK_GLOM_H
 #define NOTEBOOK_GLOM_H
 
-#include "box_db.h"
+#include "box_withbuttons.h"
 #include <glom/libglom/document/document_glom.h>
 
 namespace Glom

Added: trunk/glom/signal_reemitter.h
==============================================================================
--- (empty file)
+++ trunk/glom/signal_reemitter.h	Tue Apr 29 14:32:35 2008
@@ -0,0 +1,68 @@
+/* Glom
+ *
+ * Copyright (C) 2008 Murray Cumming
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GLOM_PRIVS_H
+#define GLOM_PRIVS_H
+
+#include <sigc++/sigc++.h>
+
+namespace Glom
+{
+
+template<class T_sig_to_emit>
+void reemit_0args(const T_sig_to_emit& sig_to_emit)
+{
+  sig_to_emit.emit();
+}
+
+template<class T_sig_to_emit, typename T_arg1>
+void reemit_1arg(T_arg1 arg1, const T_sig_to_emit& sig_to_emit)
+{
+  sig_to_emit.emit(arg1);
+}
+
+//Note that sig_to_catch is by-value instead of const-reference, 
+//because connect() is a non-const method, 
+//and a non-const-reference could not be used with a temporary instance.
+
+/** Emit a signal when another signal is emitted.
+ * @param sig_to_catch The signal to handle.
+ * @param sig_to_emit The signal to emit when @a sig_to_catch is handled.
+ */
+template<class T_sig_to_catch, class T_sig_to_emit>
+void signal_connect_for_reemit_0args(T_sig_to_catch sig_to_catch, const T_sig_to_emit& sig_to_emit)
+{
+  sig_to_catch.connect( sigc::bind( sigc::ptr_fun(&reemit_0args<T_sig_to_emit>), sig_to_emit) );
+}
+
+/** Emit a signal when another signal is emitted.
+ * @param sig_to_catch The signal to handle.
+ * @param sig_to_emit The signal to emit when @a sig_to_catch is handled.
+ */
+template<class T_arg1, class T_sig_to_emit>
+void signal_connect_for_reemit_1arg(sigc::signal1<void, T_arg1> sig_to_catch, const T_sig_to_emit& sig_to_emit)
+{
+  sig_to_catch.connect( sigc::bind( sigc::ptr_fun(&reemit_1arg<T_sig_to_emit, T_arg1>), sig_to_emit) );
+}
+
+} //namespace Glom
+
+#endif //GLOM_PRIVS_H
+

Modified: trunk/glom/utility_widgets/adddel/adddel.h
==============================================================================
--- trunk/glom/utility_widgets/adddel/adddel.h	(original)
+++ trunk/glom/utility_widgets/adddel/adddel.h	Tue Apr 29 14:32:35 2008
@@ -21,7 +21,7 @@
 #ifndef ADDDEL_H
 #define ADDDEL_H
 
-#include "gtkmm.h"
+#include <gtkmm.h>
 #include <glom/libglom/data_structure/field.h>
 #include <libgdamm.h>
 #include <libglademm.h>

Modified: trunk/glom/utility_widgets/db_adddel/db_adddel.cc
==============================================================================
--- trunk/glom/utility_widgets/db_adddel/db_adddel.cc	(original)
+++ trunk/glom/utility_widgets/db_adddel/db_adddel.cc	Tue Apr 29 14:32:35 2008
@@ -71,9 +71,10 @@
   m_bAllowUserActions(true),
   m_bPreventUserSignals(false),
   m_bIgnoreTreeViewSignals(false),
-  m_auto_add(true),
   m_allow_add(true),
   m_allow_delete(true),
+  m_find_mode(false),
+  m_allow_only_one_related_record(false),
   m_columns_ready(false),
   m_allow_view(true),
   m_allow_view_details(false),
@@ -191,18 +192,8 @@
 
 void DbAddDel::on_MenuPopup_activate_Add()
 {
-  if(m_auto_add)
-  {
-    Gtk::TreeModel::iterator iter = get_item_placeholder();
-    if(iter)
-    {
-      select_item(iter, true /* start_editing */);
-    }
-  }
-  else
-  {
-    signal_user_requested_add().emit(); //Let the client code add the row explicitly, if it wants.
-  }
+  //Create a new record in the database:
+  start_new_record();
 }
 
 void DbAddDel::on_MenuPopup_activate_Delete()
@@ -367,7 +358,7 @@
    return Gtk::TreeModel::iterator();
 }
 
-Gnome::Gda::Value DbAddDel::get_value(const Gtk::TreeModel::iterator& iter, const sharedptr<const LayoutItem_Field>& layout_item)
+Gnome::Gda::Value DbAddDel::get_value(const Gtk::TreeModel::iterator& iter, const sharedptr<const LayoutItem_Field>& layout_item) const
 {
   Gnome::Gda::Value value;
 
@@ -391,7 +382,7 @@
   return value;
 }
 
-Gnome::Gda::Value DbAddDel::get_value_key_selected()
+Gnome::Gda::Value DbAddDel::get_value_key_selected() const
 {
   Gtk::TreeModel::iterator iter = get_item_selected();
   if(iter)
@@ -402,7 +393,7 @@
     return Gnome::Gda::Value();
 }
 
-Gnome::Gda::Value DbAddDel::get_value_selected(const sharedptr<const LayoutItem_Field>& layout_item)
+Gnome::Gda::Value DbAddDel::get_value_selected(const sharedptr<const LayoutItem_Field>& layout_item) const
 {
   return get_value(get_item_selected(), layout_item);
 }
@@ -421,6 +412,21 @@
     return Gtk::TreeModel::iterator();
 }
 
+Gtk::TreeModel::iterator DbAddDel::get_item_selected() const
+{
+  Glib::RefPtr<const Gtk::TreeSelection> refTreeSelection = m_TreeView.get_selection();
+  if(refTreeSelection)
+  {
+     Glib::RefPtr<Gtk::TreeSelection> unconst = Glib::RefPtr<Gtk::TreeSelection>::cast_const(refTreeSelection);
+     return unconst->get_selected();
+  }
+
+  if(m_refListStore)
+    return m_refListStore->children().end();
+  else
+    return Gtk::TreeModel::iterator();
+}
+
 
 Gtk::TreeModel::iterator DbAddDel::get_row(const Gnome::Gda::Value& key)
 {
@@ -837,7 +843,7 @@
   //Create the Gtk ColumnRecord:
 
   Gtk::TreeModel::ColumnRecord record;
-
+    
   //Database columns:
   type_model_store::type_vec_fields fields;
   {
@@ -863,6 +869,8 @@
       }
     }
   }
+  
+  m_FieldsShown = fields; //Needed by Base_DB_Table_Data::record_new().
 
   //Find the primary key:
   int column_index_key = 0;
@@ -899,6 +907,7 @@
     m_refListStore = Glib::RefPtr<type_model_store>();
   }
 
+ 
   m_TreeView.set_model(m_refListStore);
 
 
@@ -1053,7 +1062,7 @@
       type_list_indexes list_indexes = get_data_model_column_index(layout_item);
       for(type_list_indexes::const_iterator iter = list_indexes.begin(); iter != list_indexes.end(); ++iter)
       {
-        guint treemodel_col = *iter + get_count_hidden_system_columns();
+        const guint treemodel_col = *iter + get_count_hidden_system_columns();
         treerow.set_value(treemodel_col, value);
 
         //Mark this row as not a placeholder because it has real data now.
@@ -1087,6 +1096,7 @@
 void DbAddDel::set_table_name(const Glib::ustring& table_name)
 {
   m_found_set.m_table_name = table_name;
+  Base_DB_Table::m_table_name = table_name;
 }
 
 guint DbAddDel::add_column(const sharedptr<LayoutItem>& layout_item)
@@ -1272,6 +1282,16 @@
   m_TreeView.set_headers_visible(bVal);
 }
 
+void DbAddDel::set_find_mode(bool val)
+{
+  m_find_mode = val;
+}
+  
+void DbAddDel::set_allow_only_one_related_record(bool val)
+{
+  m_allow_only_one_related_record = val;
+}
+
 
 void DbAddDel::set_column_width(guint /* col */, guint /*width*/)
 {
@@ -1355,7 +1375,7 @@
   m_pOuter = false;
 }
 
-Gnome::Gda::Value DbAddDel::treeview_get_key(const Gtk::TreeModel::iterator& row)
+Gnome::Gda::Value DbAddDel::treeview_get_key(const Gtk::TreeModel::iterator& row) const
 {
   Gnome::Gda::Value value;
 
@@ -1440,13 +1460,13 @@
          
       //Signal that a new key was added:
       //We will ignore editing of bool values in the blank row. It seems like a bad way to start a new record.
-      //m_signal_user_added.emit(row);
+      //user_added(row);
     }
     else if(bIsChange)
     {
       //Existing item changed:
 
-      m_signal_user_changed.emit(row, model_column_index);
+      user_changed(row, model_column_index);
     }
   }
 }
@@ -1480,7 +1500,7 @@
     //Is it an add or a change?:
     bool bIsAdd = false;
     bool bIsChange = false;
-    bool do_signal = true;
+    bool do_change = true;
 
     if(get_allow_user_actions()) //If add is possible:
     {
@@ -1563,7 +1583,7 @@
             }
           }
 
-          do_signal = false;
+          do_change = false;
       }
       else
       {
@@ -1581,7 +1601,7 @@
       {
         //Signal that a new key was added:
         if(m_allow_add)
-          m_signal_user_added.emit(row, model_column_index);
+          user_added(row);
       }
       else if(bIsChange)
       {
@@ -1589,24 +1609,14 @@
         //Check that it has really changed - get the last value.
         if(value != valOld)
         {
-          if(do_signal)
-            m_signal_user_changed.emit(row, model_column_index);
+          if(do_change)
+            user_changed(row, model_column_index);
         }
       }
     }
   }
 }
 
-DbAddDel::type_signal_user_added DbAddDel::signal_user_added()
-{
-  return m_signal_user_added;
-}
-
-DbAddDel::type_signal_user_changed DbAddDel::signal_user_changed()
-{
-  return m_signal_user_changed;
-}
-
 #ifndef GLOM_ENABLE_CLIENT_ONLY
 DbAddDel::type_signal_user_requested_layout DbAddDel::signal_user_requested_layout()
 {
@@ -1624,19 +1634,15 @@
   return m_signal_user_requested_edit;
 }
 
-DbAddDel::type_signal_user_requested_add DbAddDel::signal_user_requested_add()
-{
-  return m_signal_user_requested_add;
-}
 
-DbAddDel::type_signal_user_reordered_columns DbAddDel::signal_user_reordered_columns()
+DbAddDel::type_signal_script_button_clicked DbAddDel::signal_script_button_clicked()
 {
-  return m_signal_user_reordered_columns;
+  return m_signal_script_button_clicked;
 }
 
-DbAddDel::type_signal_script_button_clicked DbAddDel::signal_script_button_clicked()
+DbAddDel::type_signal_record_added DbAddDel::signal_record_added()
 {
-  return m_signal_script_button_clicked;
+  return m_signal_record_added;
 }
 
 void DbAddDel::on_treeview_button_press_event(GdkEventButton* event)
@@ -1775,7 +1781,7 @@
     }
 
     //Tell other code that something has changed, so the new column order can be serialized.
-    m_signal_user_reordered_columns.emit();
+    //TODO: If this is ever wanted: m_signal_user_reordered_columns.emit();
   }
 }
 
@@ -1785,11 +1791,6 @@
   return m_vecColumnIDs;
 }
 
-void DbAddDel::set_auto_add(bool value)
-{
-  m_auto_add = value;
-}
-
 Glib::RefPtr<Gtk::TreeModel> DbAddDel::get_model()
 {
   return m_refListStore;
@@ -1834,7 +1835,7 @@
     return Gtk::TreeModel::iterator();
 }
 
-Gnome::Gda::Value DbAddDel::get_value_key(const Gtk::TreeModel::iterator& iter)
+Gnome::Gda::Value DbAddDel::get_value_key(const Gtk::TreeModel::iterator& iter) const
 {
   return treeview_get_key(iter);
 }
@@ -1890,7 +1891,7 @@
   return true;
 }
 
-bool DbAddDel::get_view_column_index(guint model_column_index, guint& view_column_index)
+bool DbAddDel::get_view_column_index(guint model_column_index, guint& view_column_index) const
 {
   //Initialize output parameter:
   view_column_index = 0;
@@ -1912,7 +1913,7 @@
   return true;
 }
 
-guint DbAddDel::get_count_hidden_system_columns()
+guint DbAddDel::get_count_hidden_system_columns() const
 {
   return 0; //The key now has explicit API in the model.
   //return 1; //The key.
@@ -2078,6 +2079,337 @@
   m_TreeView.append_column("", m_columns_hint.m_col_hint);
 }
 
+bool DbAddDel::start_new_record()
+{
+  Gtk::TreeModel::iterator iter = get_item_placeholder();
+  if(!iter)
+    return false;
+  
+  sharedptr<LayoutItem_Field> fieldToEdit;
+
+  //Start editing in the primary key or the first cell if the primary key is auto-incremented (because there is no point in editing an auto-generated value)
+  //guint index_primary_key = 0;
+  const bool bPresent = true; //get_field_primary_key_index(index_primary_key); //If there is no primary key then the default of 0 is OK.
+  if(!bPresent)
+    return false;
+   
+  sharedptr<Field> fieldPrimaryKey = get_key_field();
+  if(fieldPrimaryKey && fieldPrimaryKey->get_auto_increment())
+  {
+    //Start editing in the first cell that is not auto_increment:
+    for(type_ColumnTypes::iterator iter = m_ColumnTypes.begin(); iter != m_ColumnTypes.end(); ++iter)
+    {
+      sharedptr<LayoutItem> layout_item = iter->m_item;
+      sharedptr<LayoutItem_Field> layout_item_field = sharedptr<LayoutItem_Field>::cast_dynamic(layout_item);
+      if(!(layout_item_field->get_full_field_details()->get_auto_increment()))
+      {
+        fieldToEdit = layout_item_field;
+        break;
+      }
+    }
+  }
+  else
+  {
+    //The primary key is not auto-increment, so start by editing it:
+    fieldToEdit = sharedptr<LayoutItem_Field>::create();
+    fieldToEdit->set_full_field_details(fieldPrimaryKey);
+  }
+
+  //std::cout << "debug: index_field_to_edit=" << index_field_to_edit << std::endl;
+
+  if(fieldToEdit)
+  {
+    select_item(iter, fieldToEdit, true /* start_editing */);
+  }
+  else
+  {
+    std::cout << "start_new_record(): no editable rows." << std::endl;
+    //The only keys are non-editable, so just add a row:
+    select_item(iter); //without start_editing.
+    //g_warning("start_new_record(): index_field_to_edit does not exist: %d", index_field_to_edit);
+  }
+  
+  return true;
+}
+
+void DbAddDel::user_changed(const Gtk::TreeModel::iterator& row, guint col)
+{
+  const Gnome::Gda::Value parent_primary_key_value = get_value_key(row);
+  sharedptr<const LayoutItem_Field> layout_field = get_column_field(col);
+
+  if(!Conversions::value_is_empty(parent_primary_key_value)) //If the record's primary key is filled in:
+  {
+    //Just update the record:
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    try
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+    {
+      
+      Glib::ustring table_name = m_found_set.m_table_name;
+      sharedptr<Field> primary_key_field;
+      Gnome::Gda::Value primary_key_value;
+
+      if(!layout_field->get_has_relationship_name())
+      {
+        table_name = m_found_set.m_table_name;
+        primary_key_field = get_key_field();
+        primary_key_value = parent_primary_key_value;
+      }
+      else
+      {
+        //If it's a related field then discover the actual table that it's in,
+        //plus how to identify the record in that table.
+        const Glib::ustring relationship_name = layout_field->get_relationship_name();
+
+        Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
+
+        sharedptr<Relationship> relationship = document->get_relationship(m_found_set.m_table_name, relationship_name);
+        if(relationship)
+        {
+          table_name = relationship->get_to_table();
+          const Glib::ustring to_field_name = relationship->get_to_field();
+          //Get the key field in the other table (the table that we will change)
+          primary_key_field = get_fields_for_table_one_field(table_name, to_field_name); //TODO_Performance.
+          if(primary_key_field)
+          {
+            //Get the value of the corresponding key in the current table (that identifies the record in the table that we will change)
+            sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
+            layout_item->set_full_field_details( document->get_field(relationship->get_from_table(), relationship->get_from_field()) );
+
+            primary_key_value = get_value_selected(layout_item);
+
+            //Note: This just uses an existing record if one already exists:
+            Gnome::Gda::Value primary_key_value_used;
+            const bool test = add_related_record_for_field(layout_field, relationship, primary_key_field, primary_key_value, primary_key_value_used);
+            if(!test)
+              return;
+
+            //Get the new primary_key_value if it has been created:
+            primary_key_value = primary_key_value_used;
+
+            //Now that the related record exists, the following code to set the value of the other field in the related field can succeed.
+          }
+          else
+          {
+            g_warning("Box_Data_List::on_flowtable_field_edited(): key not found for edited related field.");
+          }
+        }
+      }
+
+      //Update the field in the record (the record with this primary key):
+      const Gnome::Gda::Value field_value = get_value(row, layout_field);
+      //std::cout << "Box_Data_List::on_adddel_user_changed(): field_value = " << field_value.to_string() << std::endl;
+      //const sharedptr<const Field>& field = layout_field->m_field;
+      //const Glib::ustring strFieldName = layout_field->get_name();
+
+      LayoutFieldInRecord field_in_record(layout_field, m_found_set.m_table_name /* parent */, primary_key_field, primary_key_value);
+
+      //Check whether the value meets uniqueness constraints:
+      Gtk::Window* window = get_application();
+      if(!check_entered_value_for_uniqueness(m_found_set.m_table_name, row, layout_field, field_value, window))
+      {
+        //Revert to the value in the database:
+        const Gnome::Gda::Value value_old = get_field_value_in_database(field_in_record, window);
+        set_entered_field_data(row, layout_field, value_old);
+
+        return; //The value has been reverted to the value in the database.
+      }
+
+
+      const bool bTest = set_field_value_in_database(field_in_record, row, field_value, false /* don't use current calculations */, window);
+
+      //Glib::ustring strQuery = "UPDATE \"" + table_name + "\"";
+      //strQuery += " SET " +  /* table_name + "." + postgres does not seem to like the table name here */ strFieldName + " = " + field.sql(field_value);
+      //strQuery += " WHERE " + table_name + "." + primary_key_field.get_name() + " = " + primary_key_field.sql(primary_key_value);
+      //bool bTest = query_execute(strQuery);
+      if(!bTest)
+      {
+        //Update failed.
+        fill_from_database(); //Replace with correct values.
+      }
+      else
+        signal_record_changed().emit();
+    }
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    catch(const Glib::Exception& ex)
+    {
+      handle_error(ex);
+    }
+    catch(const std::exception& ex)
+    {
+      handle_error(ex);
+    }
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+  }
+  else
+  {
+    //This record probably doesn't exist yet.
+    //Add new record, which will generate the primary key:
+    user_added(row);
+    
+    const Gnome::Gda::Value primaryKeyValue = get_value_key(row); //TODO_Value
+    if(!(Conversions::value_is_empty(primaryKeyValue))) //If the Add succeeeded:
+    {
+      if(!(layout_field->get_full_field_details()->get_primary_key())) //Don't try to re-set the primary key field, because we just inserted the record with it.
+      {
+        user_changed(row, col); //Change this field in the new record.
+      }
+    }
+    else
+    {
+      //A field value was entered, but the record has not been added yet, because not enough information exists yet.
+       g_warning("Box_Data_List::on_adddel_user_changed(): debug: record not yet added.");
+    }
+  }
+}
+
+
+void DbAddDel::user_added(const Gtk::TreeModel::iterator& row)
+{
+  //Prevent impossible multiple related records:
+  if(m_allow_only_one_related_record && (get_count() > 0))
+  {
+    //Tell user that they can't do that:
+    Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Extra Related Records Not Possible")), true, Gtk::MESSAGE_WARNING);
+    dialog.set_secondary_text(_("You attempted to add a new related record, but there can only be one related record, because the relationship uses a unique key.")),
+    dialog.set_transient_for(*App_Glom::get_application());
+    dialog.run();
+    
+    return;
+  }
+  
+  //std::cout << "DbAddDel::on_adddel_user_added" << std::endl;
+
+  Gnome::Gda::Value primary_key_value;
+
+  sharedptr<Field> primary_key_field = get_key_field();
+
+  //Get the new primary key value, if one is available now:
+  if(primary_key_field->get_auto_increment())
+  {
+    //Auto-increment is awkward (we can't get the last-generated ID) with postgres, so we auto-generate it ourselves;
+    const Glib::ustring& strPrimaryKeyName = primary_key_field->get_name();
+    primary_key_value = get_next_auto_increment_value(m_found_set.m_table_name, strPrimaryKeyName);  //TODO: return a Gnome::Gda::Value of an appropriate type.
+  }
+  else
+  {
+    //Use the user-entered primary key value:
+
+    //This only works when the primary key is already stored: primary_key_value = get_value_key(row);
+    //sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
+    //layout_item->set_full_field_details(field);
+    
+    primary_key_value = get_value_key_selected();
+  }
+
+  //If no primary key value is available yet, then don't add the record yet:
+  if(!Conversions::value_is_empty(primary_key_value))
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    sharedptr<SharedConnection> sharedconnection = connect_to_server(get_application()); //Keep it alive while we need the data_model.
+#else
+    std::auto_ptr<ExceptionConnection> error;
+    sharedptr<SharedConnection> sharedconnection = connect_to_server(get_app_window(), error); //Keep it alive while we need the data_model.
+    // Ignore error, sharedconnection presence is checked below
+#endif
+    if(sharedconnection)
+    {
+      sharedptr<LayoutItem_Field> layout_field = sharedptr<LayoutItem_Field>::create();
+      layout_field->set_full_field_details(primary_key_field);
+      if(!check_entered_value_for_uniqueness(m_found_set.m_table_name, layout_field, primary_key_value, get_application()))
+      {
+        //Revert to a blank value.
+        primary_key_value = Conversions::get_empty_value(layout_field->get_full_field_details()->get_glom_type());
+        set_entered_field_data(row, layout_field, primary_key_value);
+        return;
+      }
+
+      Glib::RefPtr<Gnome::Gda::DataModel> data_model = record_new(true /* use entered field data*/, primary_key_value);
+      if(data_model)
+      {
+        //Save the primary key value for later use:
+        //record_new() did this: set_value_key(row, primary_key_value);
+
+        //Show the primary key in the row, if the primary key is visible:
+
+        //If it's an auto-increment, then get the value and show it:
+        if(primary_key_field->get_auto_increment())
+        {
+          sharedptr<LayoutItem_Field> layout_item = sharedptr<LayoutItem_Field>::create();
+          layout_item->set_full_field_details(primary_key_field);
+          set_value(row, layout_item, primary_key_value);
+        }
+
+        //Allow a parent widget to link the new record by setting the foreign key:
+        signal_record_added().emit(row, primary_key_value);
+      }
+      else
+        handle_error();
+    }
+    else
+    {
+      //Add Record failed.
+      //Replace with correct values:
+      fill_from_database();
+    }
+  }
+}
+
+void DbAddDel::user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator&  /* rowEnd TODO */)
+{
+  if(rowStart)
+  {
+    if(confirm_delete_record())
+    {
+      const Gnome::Gda::Value primary_key_value = get_primary_key_value(rowStart);
+      record_delete(primary_key_value);
+
+      //Remove the row:
+      remove_item(rowStart);
+
+      //TODO_refactor: Just emit signal_record_changed() directly instead?
+      on_record_deleted(primary_key_value);
+    }
+  }
+}
+
+//An override of the Base_DB method:
+void DbAddDel::set_entered_field_data(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value)
+{
+  return set_value_selected(field, value);
+}
+
+//An override of the Base_DB method:
+void DbAddDel::set_entered_field_data(const Gtk::TreeModel::iterator& row, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value)
+{
+  return set_value(row, field, value);
+}
+
+Gnome::Gda::Value DbAddDel::get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const
+{
+  return get_value_selected(field);
+}
+
+sharedptr<Field> DbAddDel::get_field_primary_key() const
+{
+  return get_key_field();
+}
+
+Gnome::Gda::Value DbAddDel::get_primary_key_value_selected() const
+{
+  return get_value_key_selected();
+}
+
+void DbAddDel::set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value)
+{
+  set_value_key(row, value);
+}
+
+Gnome::Gda::Value DbAddDel::get_primary_key_value(const Gtk::TreeModel::iterator& row) const
+{
+  return get_value_key(row);
+}
+
 } //namespace Glom
 
 

Modified: trunk/glom/utility_widgets/db_adddel/db_adddel.h
==============================================================================
--- trunk/glom/utility_widgets/db_adddel/db_adddel.h	(original)
+++ trunk/glom/utility_widgets/db_adddel/db_adddel.h	Tue Apr 29 14:32:35 2008
@@ -21,11 +21,12 @@
 #ifndef GLOM_DB_ADDDEL_H
 #define GLOM_DB_ADDDEL_H
 
-#include "gtkmm.h"
+#include <gtkmm.h>
 #include <glom/libglom/data_structure/layout/layoutitem_field.h>
 #include <libgdamm.h>
 #include "glom_db_treemodel.h"
 #include <glom/libglom/document/document_glom.h>
+#include <glom/base_db_table_data.h>
 
 #include <vector>
 #include <map>
@@ -56,11 +57,11 @@
 
 class DbTreeViewColumnGlom;
 
-//For adding/deleting/selecting multi-columned lists of items.
-//This was also an abstraction layer against the strangeness of GtkSheet, though it now uses Gtk::TreeView instead.
+/** For adding/deleting/selecting record rows.
+ */
 class DbAddDel
  : public Gtk::VBox,
-   public View_Composite_Glom
+   public Base_DB_Table_Data
 {
 public:
   friend class InnerIgnore; //declared below.
@@ -73,21 +74,36 @@
 
   virtual void set_allow_add(bool val = true);
   virtual void set_allow_delete(bool val = true);
+    
+  /** Prevent any attempts by this class to change actual records,
+   * if the widget is just being used to enter find critera.
+   * 
+   * @param val True if find mode should be used.
+   */
+  void set_find_mode(bool val = true);
+    
+  /** Prevent more than one record from being added,
+   * Use this if the portal is showing related records, 
+   * and if the relationship's to-field is unique or a primary key.
+   * In this case, adding a new record would require a duplicate value in that 
+   * unique field.
+   * When the user tries to do this, he will see an explanatory dialog from this 
+   * widget.
+   * 
+   * @param val True if multiple records  should be presented.
+   */
+  void set_allow_only_one_related_record(bool val = true);
+    
 
   //Gtk::TreeModel::iterator add_item(const Gnome::Gda::Value& valKey); //Return index of new row.
 
-  /** Get an iterator to the blank row in which the user should add data for the new row.
-   * You can then add the row to your underlying data store when some data has been filled, by handling signal_user_changed.
-   */
-  Gtk::TreeModel::iterator get_item_placeholder(); //Return index of the placeholder row.
-
   void remove_item(const Gtk::TreeModel::iterator& iter);
 
-  Gnome::Gda::Value get_value(const Gtk::TreeModel::iterator& iter, const sharedptr<const LayoutItem_Field>& layout_item);
+  Gnome::Gda::Value get_value(const Gtk::TreeModel::iterator& iter, const sharedptr<const LayoutItem_Field>& layout_item) const;
 
   /** Get the row's hidden key
    */
-  Gnome::Gda::Value get_value_key(const Gtk::TreeModel::iterator& iter);
+  Gnome::Gda::Value get_value_key(const Gtk::TreeModel::iterator& iter) const;
 
   /** Set the row's hidden key
    */
@@ -96,10 +112,11 @@
   /** @param col A value returned from add_column().
    * @result The value on the selected row.
    */
-  Gnome::Gda::Value get_value_selected(const sharedptr<const LayoutItem_Field>& layout_item);
-  Gnome::Gda::Value get_value_key_selected();
+  Gnome::Gda::Value get_value_selected(const sharedptr<const LayoutItem_Field>& layout_item) const;
+  Gnome::Gda::Value get_value_key_selected() const;
 
   Gtk::TreeModel::iterator get_item_selected();
+  Gtk::TreeModel::iterator get_item_selected() const; //There is no TreeModel::const_iterator
 
   /** 
    * @param iter The row to be selected. 
@@ -188,12 +205,10 @@
   void finish_editing(); //Closes active edit controls and commits the data to the cell.
   //virtual void reactivate(); //Sheet doesn't seem to update unless a cell is active.
   void set_prevent_user_signals(bool bVal = true);
+    
+  //TODO_refactor: make private.
 
-  /** When this is set to true, a new row will be added automatically, and the cursor will be placed in the first column of the new row.
-   * Use set_auto_add(false) if you want to provide default values for columns in the new row, or if you want to place the cursor in a different column.
-   * If @a value is false then signal_user_requested_add will be emitted so that you can add the row explicitly.
-   */
-  void set_auto_add(bool value = true);
+  void user_added(const Gtk::TreeModel::iterator& row);
 
   Glib::RefPtr<Gtk::TreeModel> get_model();
   Glib::RefPtr<const Gtk::TreeModel> get_model() const;
@@ -202,36 +217,43 @@
 
   //Signals:
 
-  //row number, col number.
-  typedef sigc::signal<void, const Gtk::TreeModel::iterator&, guint> type_signal_user_added;
-  type_signal_user_added signal_user_added();
-
-  //row number, col number.
-  typedef sigc::signal<void, const Gtk::TreeModel::iterator&, guint> type_signal_user_changed;
-  type_signal_user_changed signal_user_changed();
 
-  //start row, end row
+  /** 
+   * @param start_row
+   * @param end_row
+   */
   typedef sigc::signal<void, const Gtk::TreeModel::iterator&, const Gtk::TreeModel::iterator&> type_signal_user_requested_delete;
   type_signal_user_requested_delete signal_user_requested_delete();
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
+  /** Emitted when the user wants to edit the layout of the items in this widget.
+   */
   typedef sigc::signal<void> type_signal_user_requested_layout;
   type_signal_user_requested_layout signal_user_requested_layout();
 #endif // !GLOM_ENABLE_CLIENT_ONLY
 
-  //row number.
+  /** Emitted when the user request a view/edit of the details of the record.
+   * @param row
+   */
   typedef sigc::signal<void, const Gtk::TreeModel::iterator&> type_signal_user_requested_edit;
   type_signal_user_requested_edit signal_user_requested_edit();
 
-  typedef sigc::signal<void> type_signal_user_requested_add;
-  type_signal_user_requested_add signal_user_requested_add();
-
-  typedef sigc::signal<void> type_signal_user_reordered_columns;
-  type_signal_user_reordered_columns signal_user_reordered_columns();
-
+  /** Emitted when the user clicks on a script button.
+   * @param layout_button The layout item for the script button that was clicked.
+   * @param row
+   */
   typedef sigc::signal<void, const sharedptr<const LayoutItem_Button>&, const Gtk::TreeModel::iterator&> type_signal_script_button_clicked;
   type_signal_script_button_clicked signal_script_button_clicked();
 
+  /** Allow a parent widget to set the foreign key when a record is added,
+   * to make the new record a related record.
+   *
+   * @param row Row number
+   * @param primary_key_value The value of the primary key of the new related record.
+   */
+  typedef sigc::signal<void, const Gtk::TreeModel::iterator&, const Gnome::Gda::Value&> type_signal_record_added;
+  type_signal_record_added signal_record_added();
+    
  
   virtual Gtk::TreeModel::iterator get_last_row();
   virtual Gtk::TreeModel::iterator get_last_row() const;
@@ -239,6 +261,19 @@
   virtual void set_open_button_title(const Glib::ustring& title);
 
 protected:
+  
+  
+  //Overrides of Base_DB/Base_DB_Table methods:
+  virtual void set_entered_field_data(const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
+  virtual void set_entered_field_data(const Gtk::TreeModel::iterator& row, const sharedptr<const LayoutItem_Field>& field, const Gnome::Gda::Value& value);
+  virtual Gnome::Gda::Value get_entered_field_data(const sharedptr<const LayoutItem_Field>& field) const;
+
+  //Implementations of pure virtual methods from Base_DB_Table_Data:
+  virtual sharedptr<Field> get_field_primary_key() const;
+  virtual Gnome::Gda::Value get_primary_key_value_selected() const;
+  virtual void set_primary_key_value(const Gtk::TreeModel::iterator& row, const Gnome::Gda::Value& value);
+  virtual Gnome::Gda::Value get_primary_key_value(const Gtk::TreeModel::iterator& row) const;
+      
   Gtk::CellRenderer* construct_specified_columns_cellrenderer(const sharedptr<LayoutItem>& layout_item, int model_column_index, int data_model_column_index);
 
   bool get_model_column_index(guint view_column_index, guint& model_column_index);
@@ -252,7 +287,7 @@
   type_list_indexes get_data_model_column_index(const sharedptr<const LayoutItem_Field>& layout_item_field) const;
 
   virtual void setup_menu();
-  virtual Gnome::Gda::Value treeview_get_key(const Gtk::TreeModel::iterator& row);
+  virtual Gnome::Gda::Value treeview_get_key(const Gtk::TreeModel::iterator& row) const;
 
   ///Add a blank row, or return the existing blank row if there already is one.
   //virtual Gtk::TreeModel::iterator get_next_available_row_with_add_if_necessary();
@@ -298,9 +333,9 @@
   /** @param model_column_index A value returned from add_column().
    * @param view_column_index The index of the corresponding view column.
    */
-  bool get_view_column_index(guint model_column_index, guint& view_column_index);
+  bool get_view_column_index(guint model_column_index, guint& view_column_index) const;
 
-  guint get_count_hidden_system_columns();
+  guint get_count_hidden_system_columns() const;
 
   //The column_id is extra information that we can use later to discover what the column shows, even when columns have been reordered.
   guint treeview_append_column(const Glib::ustring& title, Gtk::CellRenderer& cellrenderer, int model_column_index, int data_model_column_index);
@@ -313,6 +348,7 @@
 
   int get_fixed_cell_height();
 
+  //TODO: Remove this and use AppGlom::get_application() instead?
   App_Glom* get_application();
 
   static void apply_formatting(Gtk::CellRenderer* renderer, const FieldFormatting& formatting);
@@ -357,10 +393,13 @@
 
   type_vecStrings m_vecColumnIDs; //We give each ViewColumn a special ID, so we know where they are after a reorder.
 
-  bool m_auto_add;
   bool m_allow_add;
   bool m_allow_delete;
+    
+  bool m_find_mode;
+  bool m_allow_only_one_related_record;
 
+  /// The primary key for the table:
   sharedptr<Field> m_key_field;
 
   bool m_columns_ready;
@@ -369,16 +408,16 @@
   Gtk::TreeViewColumn* m_treeviewcolumn_button;
 
   //signals:
-  type_signal_user_added m_signal_user_added;
-  type_signal_user_changed m_signal_user_changed;
   type_signal_user_requested_delete m_signal_user_requested_delete;
   type_signal_user_requested_edit m_signal_user_requested_edit;
-  type_signal_user_requested_add m_signal_user_requested_add;
 #ifndef GLOM_ENABLE_CLIENT_ONLY
   type_signal_user_requested_layout m_signal_user_requested_layout;
 #endif // !GLOM_ENABLE_CLIENT_ONLY
-  type_signal_user_reordered_columns m_signal_user_reordered_columns;
   type_signal_script_button_clicked m_signal_script_button_clicked;
+  type_signal_record_added m_signal_record_added;
+    
+  //TODO: Do this properly:
+  //type_signal_user_added m_signal_record_count_changed;
 
   bool get_ignore_treeview_signals() const;
   void set_ignore_treeview_signals(bool ignore = true);
@@ -420,6 +459,20 @@
   Glib::RefPtr<Gtk::ListStore> m_model_hint;
 
   int m_fixed_cell_height;
+    
+    
+private:
+  
+  //TODO_refactor: Give these better names, and document them:
+  bool start_new_record();
+  void user_changed(const Gtk::TreeModel::iterator& row, guint col);
+  void user_requested_delete(const Gtk::TreeModel::iterator& rowStart, const Gtk::TreeModel::iterator&  /* rowEnd TODO */);
+
+  //TODO_refactor: Make some other methods private too.
+  /** Get an iterator to the blank row in which the user should add data for the new row.
+   * You can then add the row to your underlying data store when some data has been filled, by handling signal_user_changed.
+   */
+  Gtk::TreeModel::iterator get_item_placeholder(); //Return index of the placeholder row.
 };
 
 } //namespace Glom

Modified: trunk/glom/utility_widgets/db_adddel/db_adddel_withbuttons.cc
==============================================================================
--- trunk/glom/utility_widgets/db_adddel/db_adddel_withbuttons.cc	(original)
+++ trunk/glom/utility_widgets/db_adddel/db_adddel_withbuttons.cc	Tue Apr 29 14:32:35 2008
@@ -56,18 +56,7 @@
 
 void DbAddDel_WithButtons::on_button_add()
 {
-  if(m_auto_add)
-  {
-    Gtk::TreeModel::iterator iter = get_item_placeholder();
-    if(iter)
-    {
-      select_item(iter, true /* start_editing */);
-    }
-  }
-  else
-  {
-    signal_user_requested_add().emit(); //Let the client code add the row explicitly, if it wants.
-  }
+  on_MenuPopup_activate_Add();
 }
 
 void DbAddDel_WithButtons::on_button_del()



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