[glom] Python button scripts: Add show_table_details() and show_table_list().



commit 8a1511ebc6c607892c8ba734b9ea0d8b88a91156
Author: Murray Cumming <murrayc murrayc com>
Date:   Sun Feb 28 02:29:41 2010 +0100

    Python button scripts: Add show_table_details() and show_table_list().
    
    * Makefile_libglom.am:
    * glom/python_embed/python_module/py_glom_module.cc:
    * glom/libglom/python_embed/py_glom_ui.[h|cc]: Added a PyGlomUI object
    that can be passed to python functions in addition to the record object,
    with methods to navigate in the UI. So far there is just
    show_table_details() and show_table_list().
    * glom/glom_developer.glade: Script editor window: Mention the extra ui
    parameter.
    * glom/python_embed/glom_python.[h|cc]: Add a callback object parameter so
    scripts can tell the caller to change things in the UI.
      * glom/frame_glom.h: Made show_table() public so that the Application can
    call it.
    * glom/application.[h|cc]: Add show_table_details() and add_show_list()
    methods for the callbacks to call.
    * glom/mode_data/box_data.[h|cc]: Handle the callback signals, to actually do
    the navigation when a python script requests it.
    * glom/mode_design/layout/layout_item_dialogs/dialog_buttonscript.cc: Adapted.

 ChangeLog                                          |   22 ++
 Makefile_libglom.am                                |    2 +
 glom/application.cc                                |   17 +
 glom/application.h                                 |    5 +-
 glom/frame_glom.h                                  |   14 +-
 glom/glom_developer.glade                          |    2 +-
 glom/libglom/connectionpool.cc                     |    2 +-
 glom/libglom/python_embed/py_glom_record.h         |    2 +
 glom/libglom/python_embed/py_glom_ui.cc            |   72 +++++
 glom/libglom/python_embed/py_glom_ui.h             |   72 +++++
 glom/mode_data/box_data.cc                         |   29 ++-
 glom/mode_data/box_data.h                          |    6 +-
 .../layout_item_dialogs/dialog_buttonscript.cc     |    4 +-
 glom/python_embed/glom_python.cc                   |  316 +++++++++++---------
 glom/python_embed/glom_python.h                    |   13 +-
 glom/python_embed/python_module/py_glom_module.cc  |    6 +
 16 files changed, 418 insertions(+), 166 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 0761c41..7cffdd1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2010-02-28  Murray Cumming  <murrayc murrayc com>
+
+  Python button scripts: Add show_table_details() and show_table_list().
+  
+	* Makefile_libglom.am:
+	* glom/python_embed/python_module/py_glom_module.cc:
+	* glom/libglom/python_embed/py_glom_ui.[h|cc]: Added a PyGlomUI object 
+	that can be passed to python functions in addition to the record object, 
+	with methods to navigate in the UI. So far there is just 
+	show_table_details() and show_table_list().
+	* glom/glom_developer.glade: Script editor window: Mention the extra ui 
+	parameter.
+	* glom/python_embed/glom_python.[h|cc]: Add a callback object parameter so 
+	scripts can tell the caller to change things in the UI.
+  * glom/frame_glom.h: Made show_table() public so that the Application can 
+	call it.
+	* glom/application.[h|cc]: Add show_table_details() and add_show_list() 
+	methods for the callbacks to call.
+	* glom/mode_data/box_data.[h|cc]: Handle the callback signals, to actually do 
+	the navigation when a python script requests it.
+	* glom/mode_design/layout/layout_item_dialogs/dialog_buttonscript.cc: Adapted.
+	
 2010-02-27  Murray Cumming  <murrayc murrayc com>
 
   Python: Prevent field calculations from changing the data in other fields.
diff --git a/Makefile_libglom.am b/Makefile_libglom.am
index a8aa118..63fdc75 100644
--- a/Makefile_libglom.am
+++ b/Makefile_libglom.am
@@ -148,6 +148,8 @@ glom_libglom_libglom_1_14_la_SOURCES =					\
 	glom/libglom/python_embed/py_glom_related.h			\
 	glom/libglom/python_embed/py_glom_relatedrecord.cc		\
 	glom/libglom/python_embed/py_glom_relatedrecord.h		\
+	glom/libglom/python_embed/py_glom_ui.cc		\
+	glom/libglom/python_embed/py_glom_ui.h		\
 	glom/libglom/python_embed/pygdavalue_conversions.cc		\
 	glom/libglom/python_embed/pygdavalue_conversions.h
 
diff --git a/glom/application.cc b/glom/application.cc
index eb08b2a..b4fb50d 100644
--- a/glom/application.cc
+++ b/glom/application.cc
@@ -2539,6 +2539,23 @@ void App_Glom::update_window_title()
   #endif //GLOM_ENABLE_MAEMO
 }
 
+void App_Glom::show_table_details(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value)
+{
+  if(!m_pFrame)
+    return;
+    
+  m_pFrame->show_table(table_name, primary_key_value);
+}
+
+void App_Glom::show_table_list(const Glib::ustring& table_name)
+{
+  if(!m_pFrame)
+    return;
+  
+  m_pFrame->show_table(table_name);
+}
+
+  
 
 
 } //namespace Glom
diff --git a/glom/application.h b/glom/application.h
index 6ff2a6e..c7c30a9 100644
--- a/glom/application.h
+++ b/glom/application.h
@@ -105,7 +105,10 @@ public:
 
   ///Whether to show the generated SQL queries on stdout, for debugging.
   void set_show_sql_debug(bool val = true);
-
+  
+  void show_table_details(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value);
+  void show_table_list(const Glib::ustring& table_name);
+  
   static App_Glom* get_application();
 
 protected:
diff --git a/glom/frame_glom.h b/glom/frame_glom.h
index a5afb06..26bec8c 100644
--- a/glom/frame_glom.h
+++ b/glom/frame_glom.h
@@ -186,6 +186,13 @@ public:
 
   Glib::ustring get_shown_table_name() const;
 
+  /** Show the table, possibly selecting a particular record, possibly showing that in the details tab.
+   *
+   * @param table_name The database table to show.
+   * @param primary_key_value_for_details If specified, switch to the details view, and show this record.
+   */
+  void show_table(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value_for_details = Gnome::Gda::Value());
+  
 protected:
 
   
@@ -200,13 +207,6 @@ protected:
    */
   void show_table_allow_empty(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value_for_details = Gnome::Gda::Value());
 
-  /** Show the table, possibly selecting a particular record, possibly showing that in the details tab.
-   *
-   * @param table_name The database table to show.
-   * @param primary_key_value_for_details If specified, switch to the details view, and show this record.
-   */
-  void show_table(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value_for_details = Gnome::Gda::Value());
-
   /** Hide the currently shown table so that no table is shown.
    */
   void show_no_table();
diff --git a/glom/glom_developer.glade b/glom/glom_developer.glade
index cfe5ada..4f78e6a 100644
--- a/glom/glom_developer.glade
+++ b/glom/glom_developer.glade
@@ -1089,7 +1089,7 @@
                 <property name="xalign">0</property>
                 <property name="label">import glom
 
-&lt;b&gt;def on_button_clicked(record):&lt;/b&gt;</property>
+&lt;b&gt;def on_button_clicked(record, ui):&lt;/b&gt;</property>
                 <property name="use_markup">True</property>
               </object>
               <packing>
diff --git a/glom/libglom/connectionpool.cc b/glom/libglom/connectionpool.cc
index 6152b89..6f793da 100644
--- a/glom/libglom/connectionpool.cc
+++ b/glom/libglom/connectionpool.cc
@@ -535,7 +535,7 @@ bool ConnectionPool::startup(const SlotProgress& slot_progress, bool network_sha
 
   //If we crash while running (unlikely, hopefully), then try to cleanup.
   //Comment this out if you want to see the backtrace in a debugger.
-  previous_sig_handler = signal(SIGSEGV, &on_linux_signal);
+  //previous_sig_handler = signal(SIGSEGV, &on_linux_signal);
 
   return true;
 }
diff --git a/glom/libglom/python_embed/py_glom_record.h b/glom/libglom/python_embed/py_glom_record.h
index e3e7f84..eaec45e 100644
--- a/glom/libglom/python_embed/py_glom_record.h
+++ b/glom/libglom/python_embed/py_glom_record.h
@@ -70,6 +70,8 @@ public:
   type_map_field_values m_map_field_values;
 
   Glib::RefPtr<Gnome::Gda::Connection> m_connection;
+  
+private:
   bool m_read_only;
 };
 
diff --git a/glom/libglom/python_embed/py_glom_ui.cc b/glom/libglom/python_embed/py_glom_ui.cc
new file mode 100644
index 0000000..91a8084
--- /dev/null
+++ b/glom/libglom/python_embed/py_glom_ui.cc
@@ -0,0 +1,72 @@
+/* Glom
+ *
+ * Copyright (C) 2001-2005 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 <libglom/python_embed/py_glom_ui.h>
+#include <libglom/python_embed/pygdavalue_conversions.h>
+
+
+namespace Glom
+{
+
+PythonUICallbacks::type_signal_show_table_details PythonUICallbacks::signal_show_table_details()
+{
+  return m_signal_show_table_details;
+}
+
+PythonUICallbacks::type_signal_show_table_list PythonUICallbacks::signal_show_table_list()
+{
+  return m_signal_show_table_list;
+}
+
+PyGlomUI::PyGlomUI()
+: m_callbacks(0)
+{
+}
+PyGlomUI::PyGlomUI(const PythonUICallbacks& callbacks)
+: m_callbacks(&callbacks)
+{
+}
+
+PyGlomUI::~PyGlomUI()
+{
+}
+
+void PyGlomUI::show_table_details(const std::string& table_name, const boost::python::object& primary_key_value)
+{
+  if(!m_callbacks)
+    return;
+    
+  Gnome::Gda::Value gda_primary_key_value;
+  
+  GValue value = {0, {{0}}};
+  const bool test = glom_pygda_value_from_pyobject(&value, primary_key_value);
+  if(test && G_IS_VALUE(&value))
+    gda_primary_key_value = Gnome::Gda::Value(&value);
+
+  m_callbacks->m_signal_show_table_details.emit(table_name, gda_primary_key_value);
+}
+
+void PyGlomUI::show_table_list(const std::string& table_name)
+{
+  if(m_callbacks)
+    m_callbacks->m_signal_show_table_list.emit(table_name);
+}
+
+} //namespace Glom
diff --git a/glom/libglom/python_embed/py_glom_ui.h b/glom/libglom/python_embed/py_glom_ui.h
new file mode 100644
index 0000000..0424dbd
--- /dev/null
+++ b/glom/libglom/python_embed/py_glom_ui.h
@@ -0,0 +1,72 @@
+/* Glom
+ *
+ * Copyright (C) 2001-2005 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_PYTHON_GLOM_UI_H
+#define GLOM_PYTHON_GLOM_UI_H
+
+#include <boost/python.hpp>
+
+#include <libglom/document/document.h>
+#include <libglom/data_structure/field.h>
+#include <glibmm/ustring.h>
+
+namespace Glom
+{
+
+/** UI code should connect to the signals to respond when Python code 
+ * request a change in the UI.
+ */
+class PythonUICallbacks : public sigc::trackable
+{
+public:
+  /** For example,
+   * void on_show_details(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value);
+   */
+  typedef sigc::signal<void, const Glib::ustring&, const Gnome::Gda::Value&> type_signal_show_table_details;
+  type_signal_show_table_details signal_show_table_details();
+  
+  typedef sigc::signal<void, const Glib::ustring&> type_signal_show_table_list;
+  type_signal_show_table_list signal_show_table_list();
+  
+  friend class PyGlomUI;
+  
+private:
+  type_signal_show_table_details m_signal_show_table_details;
+  type_signal_show_table_list m_signal_show_table_list;
+};
+
+class PyGlomUI
+{
+public:
+  //A default constructor seems to be necessary for boost::python
+  PyGlomUI();
+  explicit PyGlomUI(const PythonUICallbacks& callbacks);
+  ~PyGlomUI();
+  
+  void show_table_details(const std::string& table_name, const boost::python::object& primary_key_value);
+  void show_table_list(const std::string& table_name);  
+
+private:
+  const PythonUICallbacks* m_callbacks;
+};
+
+} //namespace Glom
+
+#endif //GLOM_PYTHON_GLOM_UI_H
diff --git a/glom/mode_data/box_data.cc b/glom/mode_data/box_data.cc
index 82d11aa..f875a1e 100644
--- a/glom/mode_data/box_data.cc
+++ b/glom/mode_data/box_data.cc
@@ -26,6 +26,7 @@
 #include <libglom/data_structure/layout/layoutitem_field.h>
 #include <glom/glom_privs.h>
 #include <glom/python_embed/glom_python.h>
+#include <glom/application.h>
 #include <algorithm> //For std::find()
 #include <libglom/libglom_config.h>
 #include <glibmm/i18n.h>
@@ -350,6 +351,24 @@ Glib::ustring Box_Data::get_layout_name() const
   return m_layout_name;
 }
 
+void Box_Data::on_python_requested_show_table_details(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value)
+{
+  std::cout << "debug: on_python_requested_show_table_details(): " << table_name << ", pk value: " << primary_key_value.to_string() << std::endl;
+  
+  App_Glom* app = App_Glom::get_application();
+  if(app)
+    app->show_table_details(table_name, primary_key_value);
+}
+
+void Box_Data::on_python_requested_show_table_list(const Glib::ustring& table_name)
+{
+  //std::cout << "debug: on_python_requested_show_table_list(): " << table_name << std::endl;
+  
+  App_Glom* app = App_Glom::get_application();
+  if(app)
+    app->show_table_list(table_name);
+}
+  
 void Box_Data::execute_button_script(const sharedptr<const LayoutItem_Button>& layout_item, const Gnome::Gda::Value& primary_key_value)
 {
   const sharedptr<Field> field_primary_key = get_field_primary_key();
@@ -365,11 +384,19 @@ void Box_Data::execute_button_script(const sharedptr<const LayoutItem_Button>& l
   {
 #endif // GLIBMM_EXCEPTIONS_ENABLED
 
+    //Allow this UI to respond to UI change requests from the Python code:
+    PythonUICallbacks callbacks;
+    callbacks.signal_show_table_details().connect(
+      sigc::mem_fun(*this, &Box_Data::on_python_requested_show_table_details));
+    callbacks.signal_show_table_list().connect(
+      sigc::mem_fun(*this, &Box_Data::on_python_requested_show_table_list)); 
+          
     glom_execute_python_function_implementation(layout_item->get_script(),
       field_values, //TODO: Maybe use the field's type here.
       get_document(),
       get_table_name(), field_primary_key, primary_key_value,
-      sharedconnection->get_gda_connection());
+      sharedconnection->get_gda_connection(),
+      callbacks);
 #ifndef GLIBMM_EXCEPTIONS_ENABLED
   }
 #endif // !GLIBMM_EXCEPTIONS_ENABLED
diff --git a/glom/mode_data/box_data.h b/glom/mode_data/box_data.h
index 9555e15..174f7f8 100644
--- a/glom/mode_data/box_data.h
+++ b/glom/mode_data/box_data.h
@@ -110,7 +110,11 @@ protected:
 
   //Signal handlers:
   virtual void on_Button_Find(); //only used by _Find sub-classes. Should be MI.
-
+  
+  //Signal handlers for the PyGlomUI callbacks:
+  void on_python_requested_show_table_details(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value);
+  void on_python_requested_show_table_list(const Glib::ustring& table_name);
+  
   static Glib::ustring xslt_process(const xmlpp::Document& xml_document, const std::string& filepath_xslt);
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
diff --git a/glom/mode_design/layout/layout_item_dialogs/dialog_buttonscript.cc b/glom/mode_design/layout/layout_item_dialogs/dialog_buttonscript.cc
index 6885d7f..ecce872 100644
--- a/glom/mode_design/layout/layout_item_dialogs/dialog_buttonscript.cc
+++ b/glom/mode_design/layout/layout_item_dialogs/dialog_buttonscript.cc
@@ -116,12 +116,14 @@ void Dialog_ButtonScript::on_button_test()
   //We need the connection when we run the script, so that the script may use it.
   sharedptr<SharedConnection> sharedconnection = connect_to_server(this /* parent window */);
 
+  PythonUICallbacks callbacks;
   glom_execute_python_function_implementation(calculation,
     field_values, //TODO: Maybe use the field's type here.
     document,
     m_table_name,
     sharedptr<Field>(), Gnome::Gda::Value(), // primary key - only used when setting values in the DB, which we would not encourage in a test.
-    sharedconnection->get_gda_connection());
+    sharedconnection->get_gda_connection(),
+    callbacks);
 }
 
 } //namespace Glom
diff --git a/glom/python_embed/glom_python.cc b/glom/python_embed/glom_python.cc
index 44bd9a3..dd91d98 100644
--- a/glom/python_embed/glom_python.cc
+++ b/glom/python_embed/glom_python.cc
@@ -21,6 +21,7 @@
 #include <config.h>
 //We need to include this before anything else, to avoid redefinitions:
 #include <libglom/python_embed/py_glom_record.h>
+#include <libglom/python_embed/py_glom_ui.h>
 #include <libglom/python_embed/pygdavalue_conversions.h>
 
 #include <boost/python.hpp>
@@ -163,31 +164,11 @@ bool gda_python_module_is_available()
   return module_glom != 0;
 }
 
-
-
-void glom_execute_python_function_implementation(const Glib::ustring& func_impl,
-  const type_map_fields& field_values,
+static boost::python::object glom_python_call(Field::glom_field_type result_type,
   Document* pDocument,
-  const Glib::ustring& table_name,
-  const sharedptr<const Field>& key_field,
-  const Gnome::Gda::Value& key_field_value,
-  const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection)
-{
-  glom_evaluate_python_function_implementation(Field::TYPE_TEXT, func_impl,
-     field_values, pDocument,
-     table_name, key_field, key_field_value,
-     opened_connection, false /* not read-only */);
-}
-
-Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field_type result_type,
-  const Glib::ustring& func_impl,
-  const type_map_fields& field_values,
-  Document* pDocument,
-  const Glib::ustring& table_name,
-  const sharedptr<const Field>& key_field,
-  const Gnome::Gda::Value& key_field_value,
-  const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection,
-  bool read_only)
+  const Glib::ustring& func_impl, 
+  const boost::python::object& param1, 
+  const boost::python::object& param2 = boost::python::object())
 {
   //std::cout << "glom_evaluate_python_function_implementation()" << std::endl;
   //for(type_map_fields::const_iterator iter = field_values.begin(); iter != field_values.end(); ++iter)
@@ -214,7 +195,14 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
 
   //prefix the def line:
   const Glib::ustring func_name = "glom_calc_field_value";
-  func_def = "def " + func_name + "(record):\n  import glom_" GLOM_ABI_VERSION_UNDERLINED "\n  import gda\n" + func_def;
+  Glib::ustring func_signature;
+  if(!param2.ptr())
+    func_signature = func_name + "(record)";
+  else
+    func_signature = func_name + "(record, ui)";
+  
+  func_def = "def " + func_signature + ":\n  import glom_" GLOM_ABI_VERSION_UNDERLINED "\n  import gda\n" + func_def;
+  
   //We did this in main(): Py_Initialize();
 
   boost::python::object pMain = boost::python::import("__main__");
@@ -225,7 +213,7 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
   {
      std::cerr << "glom_evaluate_python_function_implementation(): pDict is null" << std::endl;
      ShowTrace();
-     return valueResult;
+     return boost::python::object();
   }
 
   //Allow the function to import from our script library:
@@ -261,7 +249,7 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
   if(!module_glom)
   {
     g_warning("Could not import python glom module.");
-    return valueResult; // don't crash
+    return boost::python::object(); // don't crash
   }
 
   //TODO: Is this necessary?
@@ -269,7 +257,7 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
   if(!module_gda)
   {
     g_warning("Could not import python gda module.");
-    return valueResult;
+    return boost::python::object();
   }
 
   //Create the function definition:
@@ -299,164 +287,196 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
   {
     std::cerr << "glom_evaluate_python_function_implementation():  boost::python::exec() threw error_already_set when using text= " << std::endl << func_def << std::endl;
     ShowTrace();
-    return valueResult;
+    return boost::python::object();
   }
 
   if(!pyValue.ptr())
   {
     std::cerr << "glom_evaluate_python_function_implementation(): boost::python::exec failed." << std::endl;
     ShowTrace();
-    return valueResult;
+    return boost::python::object();
   }
 
   //Call the function:
+  boost::python::object pFunc;
+  try
+  {
+    pFunc = pDict[func_name.c_str()];
+  }
+  catch(const boost::python::error_already_set& ex)
+  {
+    std::cerr << "glom_evaluate_python_function_implementation():  pDict[func_name] threw error_already_set when func_name= " << std::endl << func_name << std::endl;
+    ShowTrace();
+    return boost::python::object();
+  }
+
+  if(!pFunc.ptr())
   {
-    boost::python::object pFunc;
-    try
+    std::cerr << "glom_evaluate_python_function_implementation(): pDict[func_name] failed." << std::endl;
+    HandlePythonError();
+    return boost::python::object();
+  }
+
+  if(!PyCallable_Check(pFunc.ptr()))
+  {
+    HandlePythonError();
+    g_warning("pFunc is not callable.");
+    return boost::python::object();
+  }
+
+  //Call the function with the parameters:
+  boost::python::object pyResultCpp;
+
+  try
+  {
+    if(!param2.ptr())
     {
-      pFunc = pDict[func_name.c_str()];
+      pyResultCpp = pFunc(param1);
     }
-    catch(const boost::python::error_already_set& ex)
+    else
     {
-      std::cerr << "glom_evaluate_python_function_implementation():  pDict[func_name] threw error_already_set when func_name= " << std::endl << func_name << std::endl;
-      ShowTrace();
-      return valueResult;
+        pyResultCpp = pFunc(param1, param2);
     }
+  }
+  catch(const boost::python::error_already_set& ex)
+  {
+    std::cerr << "Glom: Exception caught from pFunc(objRecord). func_name=" << std::endl << func_name << std::endl;
+    ShowTrace();
+  }
 
-    if(!pFunc.ptr())
-    {
-      std::cerr << "glom_evaluate_python_function_implementation(): pDict[func_name] failed." << std::endl;
-      HandlePythonError();
-      return valueResult;
-    }
+  if(!(pyResultCpp.ptr()))
+  {
+    g_warning("pyResult.ptr() was null");
+    HandlePythonError();
+  }
+  
+  //TODO: Why do we do this?
+  Py_FlushLine();
+  PyErr_Clear();
+  
+  //We did this in main(): Py_Finalize();
 
-    if(!PyCallable_Check(pFunc.ptr()))
-    {
-      HandlePythonError();
-      g_warning("pFunc is not callable.");
-      return valueResult;
-    }
+  return pyResultCpp;
+}
 
-    //The function's parameter:
+void glom_execute_python_function_implementation(const Glib::ustring& func_impl,
+  const type_map_fields& field_values,
+  Document* pDocument,
+  const Glib::ustring& table_name,
+  const sharedptr<const Field>& key_field,
+  const Gnome::Gda::Value& key_field_value,
+  const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection,
+  const PythonUICallbacks& callbacks)
+{
+  boost::python::object objRecord(new PyGlomRecord);
+  boost::python::extract<PyGlomRecord*> extractor(objRecord);
+  if(!extractor.check())
+  {
+    std::cerr << ("extract<PyGlomRecord*> failed.") << std::endl;
+  }
+  
+  PyGlomRecord* pParam = extractor;
+  if(pParam)
+  {
+    //Fill the record's details:
+    PyGlomRecord_SetFields(pParam, field_values, pDocument, table_name, key_field, key_field_value, opened_connection);
+    pParam->set_read_only();
+  }
+  
+  //Pass an additional ui parameter for use by scripts:
+  boost::python::object objUI(new PyGlomUI(callbacks));
 
-    //PyObject* pParam = PyString_FromString("test value"); //This test did not need the extra ref.
+  glom_python_call(Field::TYPE_TEXT, pDocument, func_impl, objRecord, objUI);
+}
 
-    boost::python::object objRecord(new PyGlomRecord);
-    boost::python::extract<PyGlomRecord*> extractor(objRecord);
-    if(!extractor.check())
+Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field_type result_type,
+  const Glib::ustring& func_impl,
+  const type_map_fields& field_values,
+  Document* pDocument,
+  const Glib::ustring& table_name,
+  const sharedptr<const Field>& key_field,
+  const Gnome::Gda::Value& key_field_value,
+  const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection)
+{
+  boost::python::object objRecord(new PyGlomRecord);
+  boost::python::extract<PyGlomRecord*> extractor(objRecord);
+  if(!extractor.check())
+  {
+    std::cerr << ("extract<PyGlomRecord*> failed.") << std::endl;
+    return Gnome::Gda::Value();
+  }
+  
+  PyGlomRecord* pParam = extractor;
+  if(pParam)
+  {
+    //Fill the record's details:
+    PyGlomRecord_SetFields(pParam, field_values, pDocument, table_name, key_field, key_field_value, opened_connection);
+  }
+  
+  const boost::python::object pyResultCpp = glom_python_call(result_type, pDocument, func_impl, objRecord);
+  
+  //Deal with the various possible return types:
+  Gnome::Gda::Value valueResult;
+  bool object_is_gda_value = false;
+
+  GValue value = {0, {{0}}};
+  const bool test = glom_pygda_value_from_pyobject(&value, pyResultCpp);
+
+  if(test)
+    object_is_gda_value = true;
+
+  if(object_is_gda_value && G_IS_VALUE(&value))
+  {
+    valueResult = Gnome::Gda::Value(&value);
+    //Make sure that the value is of the expected Gda type:
+    //TODO_Performance:
+    valueResult = Glom::Conversions::convert_value(valueResult, result_type);
+    //std::cout << "DEBUG: glom_evaluate_python_function_implementation(): valueResult Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
+    g_value_unset(&value);
+  }
+  else
+  {
+    //g_warning("debug: pyResult is not a gda.value");
+
+    //For instance, if one of the fields was empty, then the calculation might want to return an empty value,
+    //instead of returning 0.
+    if(pyResultCpp == boost::python::object()) //Check if it is PyNone
     {
-      std::cerr << ("extract<PyGlomRecord*> failed.") << std::endl;
-      return valueResult;
+      //The result should be an appropriate empty value for this field type:
+      valueResult = Conversions::get_empty_value(result_type);
+      //std::cout << "DEBUG: glom_evaluate_python_function_implementation(): empty value Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
     }
-
-    PyGlomRecord* pParam = extractor;
-    if(pParam)
+    else
     {
-      //Fill the record's details:
-      PyGlomRecord_SetFields(pParam, field_values, pDocument, table_name, key_field, key_field_value, opened_connection);
-      if(read_only)
-        pParam->set_read_only();
-
-      //Call the function with this parameter:
-       boost::python::object pyResultCpp;
+      //TODO: Handle numeric/date/time types:
+      //(though I don't think this code is ever reached. murrayc)
 
+      //Treat this as a string or something that can be converted to a string:
+      const char* pchResult = 0;
       try
       {
-        pyResultCpp = pFunc(objRecord);
+        pchResult = boost::python::extract<const char*>(pyResultCpp);
       }
       catch(const boost::python::error_already_set& ex)
       {
-        std::cerr << "Glom: Exception caught from pFunc(objRecord). func_name=" << std::endl << func_name << std::endl;
+        std::cerr << "Glom: Exception caught from boost::python::extract() while converting result to a const char*." << std::endl;
         ShowTrace();
+        return valueResult;
       }
 
-      if(!(pyResultCpp.ptr()))
+      if(pchResult)
       {
-        g_warning("pyResult.ptr() was null");
-        HandlePythonError();
+        bool success = false;
+        valueResult = Conversions::parse_value(result_type, pchResult, success, true /* iso_format */);
+        std::cout << "DEBUG: glom_evaluate_python_function_implementation(): parsed value Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
       }
       else
-      {
-        //Deal with the various possible return types:
-        bool object_is_gda_value = false;
-
-        GValue value = {0, {{0}}};
-        const bool test = glom_pygda_value_from_pyobject(&value, pyResultCpp);
-
-        if(test)
-          object_is_gda_value = true;
-
-        if(object_is_gda_value && G_IS_VALUE(&value))
-        {
-          valueResult = Gnome::Gda::Value(&value);
-          //Make sure that the value is of the expected Gda type:
-          //TODO_Performance:
-          valueResult = Glom::Conversions::convert_value(valueResult, result_type);
-          //std::cout << "DEBUG: glom_evaluate_python_function_implementation(): valueResult Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
-          g_value_unset(&value);
-        }
-        else
-        {
-          //g_warning("debug: pyResult is not a gda.value");
-
-          //For instance, if one of the fields was empty, then the calculation might want to return an empty value,
-          //instead of returning 0.
-          if(pyResultCpp == boost::python::object()) //Check if it is PyNone
-          {
-            //The result should be an appropriate empty value for this field type:
-            valueResult = Conversions::get_empty_value(result_type);
-            //std::cout << "DEBUG: glom_evaluate_python_function_implementation(): empty value Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
-          }
-          else
-          {
-            //TODO: Handle numeric/date/time types:
-            //(though I don't think this code is ever reached. murrayc)
-
-            //Treat this as a string or something that can be converted to a string:
-            const char* pchResult = 0;
-            try
-            {
-              pchResult = boost::python::extract<const char*>(pyResultCpp);
-            }
-            catch(const boost::python::error_already_set& ex)
-            {
-              std::cerr << "Glom: Exception caught from boost::python::extract() while converting result to a const char*." << std::endl << func_name << std::endl;
-              ShowTrace();
-              return valueResult;
-            }
-
-            if(pchResult)
-            {
-              bool success = false;
-              valueResult = Conversions::parse_value(result_type, pchResult, success, true /* iso_format */);
-              std::cout << "DEBUG: glom_evaluate_python_function_implementation(): parsed value Gda type=" << g_type_name(valueResult.get_value_type()) << std::endl;
-            }
-            else
-              HandlePythonError();
-          }
-        }
-      }
+        HandlePythonError();
     }
   }
-
-  Py_FlushLine();
-  PyErr_Clear();
-
-
-  //We did this in main(): Py_Finalize();
-
+  
   return valueResult;
 }
 
-/*
-
-examples:
-
-  return record.fields["name_first"] + " " + record.fields["name_last"]
-
-  return record.related_records["contacts"][0].fields["name_first"]
-
-  return record.related_records["invoice lines"].sum("cost") )
-*/
 
 } //namespace Glom
diff --git a/glom/python_embed/glom_python.h b/glom/python_embed/glom_python.h
index 525b68c..f45abb4 100644
--- a/glom/python_embed/glom_python.h
+++ b/glom/python_embed/glom_python.h
@@ -23,6 +23,7 @@
 
 #include <libglom/data_structure/field.h>
 #include <libglom/document/document.h>
+#include <libglom/python_embed/py_glom_ui.h>
 #include <glibmm/ustring.h>
 
 namespace Glom
@@ -41,7 +42,8 @@ bool gda_python_module_is_available();
 typedef std::map<Glib::ustring, Gnome::Gda::Value> type_map_fields;
 
 /** Run a script, ignoring the python return value.
- * The record object will be writable.
+ * The record object will be writable and the function will receive a ui 
+ * parameter so it can control navigation in the UI.
  */
 void glom_execute_python_function_implementation(const Glib::ustring& func_impl,
   const type_map_fields& field_values,
@@ -49,10 +51,12 @@ void glom_execute_python_function_implementation(const Glib::ustring& func_impl,
   const Glib::ustring& table_name,
   const sharedptr<const Field>& key_field,
   const Gnome::Gda::Value& key_field_value,
-  const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection);
+  const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection,
+  const PythonUICallbacks& callbacks);
 
 /** Run a python calculation, returning the python return value.
- * The record object will be read only unless @a read_only=false.
+ * @param for_script: If this is true then the record object will be writable, 
+ * and the function will receive a ui parameter so it can control navigation in the UI.
  */
 Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field_type result_type,
   const Glib::ustring& func_impl,
@@ -61,8 +65,7 @@ Gnome::Gda::Value glom_evaluate_python_function_implementation(Field::glom_field
   const Glib::ustring& table_name,
   const sharedptr<const Field>& key_field,
   const Gnome::Gda::Value& key_field_value,
-  const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection, 
-  bool read_only = true);
+  const Glib::RefPtr<Gnome::Gda::Connection>& opened_connection);
 
 } //namespace Glom
 
diff --git a/glom/python_embed/python_module/py_glom_module.cc b/glom/python_embed/python_module/py_glom_module.cc
index 155d717..32c08af 100644
--- a/glom/python_embed/python_module/py_glom_module.cc
+++ b/glom/python_embed/python_module/py_glom_module.cc
@@ -27,6 +27,7 @@
 #include <libglom/python_embed/py_glom_record.h>
 #include <libglom/python_embed/py_glom_related.h>
 #include <libglom/python_embed/py_glom_relatedrecord.h>
+#include <libglom/python_embed/py_glom_ui.h>
 
 using namespace Glom;
 
@@ -55,4 +56,9 @@ BOOST_PYTHON_MODULE(glom_1_14)
     .def("__getitem__", &PyGlomRelatedRecord::getitem)
     .def("__len__", &PyGlomRelatedRecord::len)
   ;
+  
+  boost::python::class_<PyGlomUI>("UI")
+    .def("show_table_details", &PyGlomUI::show_table_details)
+    .def("show_table_list", &PyGlomUI::show_table_list)
+  ;
 }



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