[gnumeric] solver: ask user to locate binaries when our search fails.



commit 981b2a00b32d4677ffeac6c8a2f73975071bb134
Author: Morten Welinder <terra gnome org>
Date:   Sat Jul 3 23:13:10 2010 -0400

    solver: ask user to locate binaries when our search fails.

 NEWS                                |    4 +
 plugins/glpk/gnm-glpk.c             |   44 ++++++-
 plugins/lpsolve/gnm-lpsolve.c       |   50 ++++++--
 schemas/gnumeric-plugins.schemas.in |   23 ++++-
 src/dialogs/ChangeLog               |   65 +++++-----
 src/dialogs/dialog-solver.c         |   25 +++-
 src/gnm-plugin.c                    |    5 +-
 src/gnumeric-gconf.c                |  235 +++++++++++++++++++++++------------
 src/gnumeric-gconf.h                |   10 ++
 src/ssconvert.c                     |    5 +-
 src/tools/ChangeLog                 |   14 ++-
 src/tools/gnm-solver.c              |   71 ++++++++++-
 src/tools/gnm-solver.h              |   11 ++-
 13 files changed, 416 insertions(+), 146 deletions(-)
---
diff --git a/NEWS b/NEWS
index 3ea267d..a1277fb 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,10 @@ Andreas:
 	* Improve function syntax tooltips. [#623317]
 	* Add weekend specifier to WORKDAY and NETWORKDAYS. [#172458]
 
+Morten:
+	* Ask user to locate solver binaries when plain search fails.
+	[#619519]
+
 --------------------------------------------------------------------------
 Gnumeric 1.10.7
 
diff --git a/plugins/glpk/gnm-glpk.c b/plugins/glpk/gnm-glpk.c
index 0428188..23623b2 100644
--- a/plugins/glpk/gnm-glpk.c
+++ b/plugins/glpk/gnm-glpk.c
@@ -6,6 +6,7 @@
 #include "value.h"
 #include "ranges.h"
 #include "gutils.h"
+#include "gnumeric-gconf.h"
 #include <gsf/gsf-impl-utils.h>
 #include <gsf/gsf-input-textline.h>
 #include <gsf/gsf-input-stdio.h>
@@ -259,10 +260,15 @@ gnm_glpk_start (GnmSolver *sol, WorkbookControl *wbc, GError **err,
 	gchar *argv[7];
 	int argc = 0;
 	GnmSolverParameters *param = sol->params;
+	const char *binary;
 
 	g_return_val_if_fail (sol->status == GNM_SOLVER_STATUS_PREPARED, FALSE);
 
-	argv[argc++] = (gchar *)SOLVER_PROGRAM;
+	binary = gnm_conf_get_plugin_glpk_glpsol_path ();
+	if (binary == NULL || *binary == 0)
+		binary = SOLVER_PROGRAM;
+
+	argv[argc++] = (gchar *)binary;
 	argv[argc++] = (gchar *)(param->options.automatic_scaling
 				 ? "--scale"
 				 : "--noscale");
@@ -306,15 +312,39 @@ gnm_glpk_stop (GnmSolver *sol, GError *err, GnmGlpk *lp)
 }
 
 gboolean
-glpk_solver_factory_functional (GnmSolverFactory *factory);
+glpk_solver_factory_functional (GnmSolverFactory *factory,
+				WBCGtk *wbcg);
 
 gboolean
-glpk_solver_factory_functional (GnmSolverFactory *factory)
+glpk_solver_factory_functional (GnmSolverFactory *factory,
+				WBCGtk *wbcg)
 {
-	char *full_path = g_find_program_in_path (SOLVER_PROGRAM);
-	gboolean res= (full_path != NULL);
-	g_free (full_path);
-	return res;
+	const char *full_path = gnm_conf_get_plugin_glpk_glpsol_path ();
+	char *path;
+
+	if (full_path && *full_path)
+		return g_file_test (full_path, G_FILE_TEST_IS_EXECUTABLE);
+
+	path = g_find_program_in_path (SOLVER_PROGRAM);
+	if (path) {
+		g_free (path);
+		return TRUE;
+	}
+
+	if (!wbcg)
+		return FALSE;
+
+	path = gnm_sub_solver_locate_binary (SOLVER_PROGRAM,
+					     "Gnu Linear Programming Kit",
+					     SOLVER_URL,
+					     wbcg);
+	if (path) {
+		gnm_conf_set_plugin_glpk_glpsol_path (path);
+		g_free (path);
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 
diff --git a/plugins/lpsolve/gnm-lpsolve.c b/plugins/lpsolve/gnm-lpsolve.c
index 4d89630..4f73d78 100644
--- a/plugins/lpsolve/gnm-lpsolve.c
+++ b/plugins/lpsolve/gnm-lpsolve.c
@@ -5,11 +5,12 @@
 #include "sheet.h"
 #include "value.h"
 #include "ranges.h"
+#include "gnumeric-gconf.h"
 #include <gsf/gsf-impl-utils.h>
 #include <glib/gi18n-lib.h>
 #include <string.h>
 
-#define SOLVER_PROGRAM "lp_solve"
+#define SOLVER_PROGRAM "lp_solveXXX"
 #define SOLVER_URL "http://sourceforge.net/projects/lpsolve/";
 #define PRIVATE_KEY "::lpsolve::"
 
@@ -255,10 +256,15 @@ gnm_lpsolve_start (GnmSolver *sol, WorkbookControl *wbc, GError **err,
 	gchar *argv[5];
 	int argc = 0;
 	GnmSolverParameters *param = sol->params;
+	const char *binary;
 
 	g_return_val_if_fail (sol->status == GNM_SOLVER_STATUS_PREPARED, FALSE);
 
-	argv[argc++] = (gchar *)SOLVER_PROGRAM;
+	binary = gnm_conf_get_plugin_lpsolve_lpsolve_path ();
+	if (binary == NULL || *binary == 0)
+		binary = SOLVER_PROGRAM;
+
+	argv[argc++] = (gchar *)binary;
 	argv[argc++] = (gchar *)"-i";
 	argv[argc++] = (gchar *)(param->options.automatic_scaling
 				 ? "-s1"
@@ -300,15 +306,39 @@ gnm_lpsolve_stop (GnmSolver *sol, GError *err, GnmLPSolve *lp)
 }
 
 gboolean
-lpsolve_solver_factory_functional (GnmSolverFactory *factory);
+lpsolve_solver_factory_functional (GnmSolverFactory *factory,
+				   WBCGtk *wbcg);
 
 gboolean
-lpsolve_solver_factory_functional (GnmSolverFactory *factory)
+lpsolve_solver_factory_functional (GnmSolverFactory *factory,
+				   WBCGtk *wbcg)
 {
-	char *full_path = g_find_program_in_path (SOLVER_PROGRAM);
-	gboolean res= (full_path != NULL);
-	g_free (full_path);
-	return res;
+	const char *full_path = gnm_conf_get_plugin_lpsolve_lpsolve_path ();
+	char *path;
+
+	if (full_path && *full_path)
+		return g_file_test (full_path, G_FILE_TEST_IS_EXECUTABLE);
+
+	path = g_find_program_in_path (SOLVER_PROGRAM);
+	if (path) {
+		g_free (path);
+		return TRUE;
+	}
+
+	if (!wbcg)
+		return FALSE;
+
+	path = gnm_sub_solver_locate_binary (SOLVER_PROGRAM,
+					     "LP Solve",
+					     SOLVER_URL,
+					     wbcg);
+	if (path) {
+		gnm_conf_set_plugin_lpsolve_lpsolve_path (path);
+		g_free (path);
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 
@@ -319,8 +349,8 @@ GnmSolver *
 lpsolve_solver_factory (GnmSolverFactory *factory, GnmSolverParameters *params)
 {
 	GnmSolver *res = g_object_new (GNM_SUB_SOLVER_TYPE,
-					  "params", params,
-					  NULL);
+				       "params", params,
+				       NULL);
 	GnmLPSolve *lp = g_new0 (GnmLPSolve, 1);
 
 	lp->parent = GNM_SUB_SOLVER (res);
diff --git a/schemas/gnumeric-plugins.schemas.in b/schemas/gnumeric-plugins.schemas.in
index 0e5adc2..bd93585 100644
--- a/schemas/gnumeric-plugins.schemas.in
+++ b/schemas/gnumeric-plugins.schemas.in
@@ -11,6 +11,27 @@
         <long>This setting determines whether created LaTeX files use UTF-8 (unicode) or ISO-8859-1 (Latin1). To use the UTF-8 files, you must have the ucs LaTeX package installed.</long>
         </locale>
       </schema>
+      <schema>
+        <key>/schemas/apps/gnumeric/plugin/lpsolve/lpsolve-path</key>
+        <applyto>/apps/gnumeric/plugin/lpsolve/lpsolve-path</applyto>
+        <owner>Gnumeric</owner>
+        <type>string</type>
+	<default/>
+        <locale name="C">
+        <short>Full path of lp_solve program to use</short>
+        <long>This is the full path to the lp_solve binary that the lpsolve plugin should use.</long>
+        </locale>
+      </schema>
+      <schema>
+        <key>/schemas/apps/gnumeric/plugin/glpk/glpsol-path</key>
+        <applyto>/apps/gnumeric/plugin/glpk/glpsol-path</applyto>
+        <owner>Gnumeric</owner>
+        <type>string</type>
+	<default/>
+        <locale name="C">
+        <short>Full path of glpsol program to use</short>
+        <long>This is the full path to the glpsol binary that the lpsolve plugin should use.</long>
+        </locale>
+      </schema>
    </schemalist>
 </gconfschemafile>
-  
diff --git a/src/dialogs/ChangeLog b/src/dialogs/ChangeLog
index 73899a9..91a1426 100644
--- a/src/dialogs/ChangeLog
+++ b/src/dialogs/ChangeLog
@@ -1,7 +1,12 @@
+2010-07-03  Morten Welinder  <terra gnome org>
+
+	* dialog-solver.c (run_solver): Check that the solver is
+	functional.  Fix error message.
+
 2010-06-30  Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-search-replace.c (dialog_search_replace_save_in_prefs): new
-	(apply_clicked): call dialog_search_replace_save_in_prefs if 
+	(apply_clicked): call dialog_search_replace_save_in_prefs if
 	  appropriate
 	(dialog_search_replace): set the current state according to the
 	  preferences
@@ -18,7 +23,7 @@
 
 2010-06-24  Andreas J. Guelzow <aguelzow pyrshep ca>
 
-	* dialog-stf.h (FormatInfo_t): add new field 
+	* dialog-stf.h (FormatInfo_t): add new field
 	* dialog-stf.c (stf_dialog): transfer autofit array
 	* dialog-stf-format-page.c (cb_col_check_clicked): set
 	  sensitivity of autofit check
@@ -26,13 +31,13 @@
 	(format_page_update_preview): handle new field
 	(stf_dialog_format_page_cleanup): ditto
 	(stf_dialog_format_page_init): ditto
-	
+
 2010-06-22  Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-sheetobject-size.c (set_params): new
 	(cb_dialog_so_size_apply_clicked): use cmd_generic instead of
 	  cmd_so_rename
-	
+
 2010-06-21  Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-solver.c (dialog_init): setup first entry
@@ -40,12 +45,12 @@
 2010-06-21  Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-formula-guru.c (real_start_editing_cb): deleted
-	(start_editing_cb): include real_start_editing_cb here 
+	(start_editing_cb): include real_start_editing_cb here
 	  (removes gtk 2.2 hack)
 
 2010-06-17  Andreas J. Guelzow <aguelzow pyrshep ca>
 
-	* dialog-formula-guru.c (cb_dialog_formula_guru_query_tooltip): 
+	* dialog-formula-guru.c (cb_dialog_formula_guru_query_tooltip):
 	  adjust tooltip
 
 2010-06-17  Andreas J. Guelzow <aguelzow pyrshep ca>
@@ -60,11 +65,11 @@
 	(cb_dialog_formula_guru_destroy): unref tooltip widgets
 	(cb_dialog_formula_guru_query_tooltip): create our own tooltip
 	(dialog_formula_guru_init): initialize tooltip widgets
-	
+
 2010-06-16  Morten Welinder <terra gnome org>
 
 	* Release 1.10.6
-	
+
 2010-06-16  Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-formula-guru.c (dialog_formula_guru_adjust_children): use
@@ -78,10 +83,10 @@
 
 2010-06-15  Andreas J. Guelzow <aguelzow pyrshep ca>
 
-	* dialog-formula-guru.c (dialog_formula_guru_adjust_children): load 
+	* dialog-formula-guru.c (dialog_formula_guru_adjust_children): load
 	  tooltip
 	(dialog_formula_guru_init): set up tooltips
-	
+
 2010-06-15  Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialogs.h (dialog_formula_guru): change storage class of an arg
@@ -95,25 +100,25 @@
 
 2010-06-10 Andreas J. Guelzow <aguelzow pyrshep ca>
 
-	* dialog-function-select.c 
+	* dialog-function-select.c
 	(dialog_function_select_get_description): rename to
 	  dialog_function_select_get_description
 	(dialog_function_select_get_description): new
 	(dialog_function_select_load_tree): store the PangoAttrList
-	(dialog_function_select_init): set up new attribute field in 
+	(dialog_function_select_init): set up new attribute field in
 	  the liststore
-	
+
 2010-06-09 Andreas J. Guelzow <aguelzow pyrshep ca>
 
-	* dialog-function-select.c 
-	(dialog_function_select_erase_search_entry): only include for 
+	* dialog-function-select.c
+	(dialog_function_select_erase_search_entry): only include for
 	  newer gtk
-	
+
 2010-06-09 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-function-select.c (dialog_function_select_init):
 	  change the dialog title to reflect help mode
-	(dialog_function_select): use a differnt key in help mode to 
+	(dialog_function_select): use a differnt key in help mode to
 	  avoid confusion
 
 2010-06-09 Andreas J. Guelzow <aguelzow pyrshep ca>
@@ -122,7 +127,7 @@
 	* dialog-function-select.c (dialog_function_select_init):
 	  show and hide the appropriate widgets depending on whether
 	  we are in help mode
-	(cb_dialog_function_select_ok_clicked): only called the 
+	(cb_dialog_function_select_ok_clicked): only called the
 	  formula guru if we aren't in help mode
 	(dialog_function_select): don't show the purposefully hidden
 	  widgets
@@ -130,16 +135,16 @@
 2010-06-09 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-function-select.c (cb_unref): new
-	(cb_dialog_function_select_search_all): handle "in use" 
+	(cb_dialog_function_select_search_all): handle "in use"
 	  selection
 	(dialog_function_select_search): set up "in use" selection
 	(cb_dialog_function_select_destroy): unref funcs
 	(dialog_function_select_load_cb): add "in use" item
 	(dialog_function_select_get_description): load stubs
-	(dialog_function_select_load_tree): ref the funcs when 
+	(dialog_function_select_load_tree): ref the funcs when
 	  storing them in the model
 	(dialog_function_select_init): setup "in use" flag
-	
+
 2010-06-08 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-function-select.c (dialog_function_select_search):
@@ -163,9 +168,9 @@
 
 2010-06-07 Andreas J. Guelzow <aguelzow pyrshep ca>
 
-	* dialog-define-names.c (cb_name_guru_name_edited): the 
+	* dialog-define-names.c (cb_name_guru_name_edited): the
 	  name becomes non-ediatable after it has been set.
-	
+
 2010-06-06 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* regression.glade: add new checkbox and move the selectors for
@@ -186,7 +191,7 @@
 	* dialog-define-names.c (name_guru_parse_pos_init): new
 	(name_guru_check_expression): use name_guru_parse_pos_init
 	(cb_name_guru_name_edited): don't duplicate errorchecks already
-	  in cmd_define_name but make sure that we don't just redefine 
+	  in cmd_define_name but make sure that we don't just redefine
 	  names
 
 2010-06-04 Andreas J. Guelzow <aguelzow pyrshep ca>
@@ -198,13 +203,13 @@
 
 2010-06-04 Andreas J. Guelzow <aguelzow pyrshep ca>
 
-	* dialog-define-names.c (name_guru_delete): no need to 
+	* dialog-define-names.c (name_guru_delete): no need to
 	  check for in use if it wasn't saved.
 	(name_guru_find_place): new
 	(name_guru_move_record): use name_guru_find_place
 	(cb_name_guru_name_edited): we also need to set the GnmNamedExpr
 	  if we possibly added a new name.
-	
+
 2010-06-04 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-define-names.c (cb_name_guru_search): make searching
@@ -227,7 +232,7 @@
 	 GtkTreeStore and GtkTreeView and adjust code throughout.
 	(name_guru_store_names): new type adjustment for every name
 	(cb_name_guru_content_edited): don't leak GnmExprTop
-	
+
 2010-06-03 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-define-names.c (cb_name_guru_switch_scope): use
@@ -245,7 +250,7 @@
 2010-06-03 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialog-define-names.c (name_guru_paste): don't overwrite text
-	
+
 2010-06-03 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* paste-names.glade: deleted
@@ -284,11 +289,11 @@
 	  ditto
 	(dialog_sign_test_tool): ditto
 	* dialog-analysis-tools.c (tool_setup_update): new
-	
+
 2010-05-31 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* dialogs.h (dialog_sign_test_two_tool): new
-	* dialog-analysis-tool-sign-test.c 
+	* dialog-analysis-tool-sign-test.c
 	(sign_test_tool_update_common_sensitivity_cb): new
 	(sign_test_tool_update_sensitivity_cb): split
 	(sign_test_two_tool_update_sensitivity_cb): new
diff --git a/src/dialogs/dialog-solver.c b/src/dialogs/dialog-solver.c
index 4502da3..7c4b778 100644
--- a/src/dialogs/dialog-solver.c
+++ b/src/dialogs/dialog-solver.c
@@ -628,12 +628,13 @@ run_solver (SolverState *state, GnmSolverParameters *param)
 	GnmSolverResult *res = NULL;
 	int y;
 
-	sol = param->options.algorithm
-		? gnm_solver_factory_create (param->options.algorithm, param)
-		: NULL;
+	sol = gnm_solver_factory_functional (param->options.algorithm,
+					     state->wbcg)
+	    ? gnm_solver_factory_create (param->options.algorithm, param)
+	    : NULL;
 	if (!sol) {
 		go_gtk_notice_dialog (top, GTK_MESSAGE_ERROR,
-				      _("No suitable solver available."));
+				      _("The chosen solver is not functional."));
 		goto fail;
 	}
 
@@ -1189,19 +1190,29 @@ dialog_solver (WBCGtk *wbcg, Sheet *sheet)
 {
         SolverState *state;
 	GnmSolverParameters *old_params = sheet->solver_parameters;
+	gboolean got_it;
+	int pass;
 
 	/* Only pop up one copy per workbook */
 	if (gnumeric_dialog_raise_if_exists (wbcg, SOLVER_KEY))
 		return;
 
-	/* First time around, pick a functional algorithm.  */
-	if (!gnm_solver_factory_functional (old_params->options.algorithm)) {
+	/*
+	 * First time around, pick a functional algorithm preferably one we
+	 * can determine is functional without asking the user anything.
+	 */
+	got_it = gnm_solver_factory_functional (old_params->options.algorithm,
+						NULL);
+	for (pass = 1; !got_it && pass <= 2; pass++) {
 		GSList *l;
+		WBCGtk *wbcg2 = pass == 2 ? wbcg : NULL;
+
 		for (l = gnm_solver_db_get (); l; l = l->next) {
 			GnmSolverFactory *factory = l->data;
 			if (old_params->options.model_type != factory->type)
 				continue;
-			if (gnm_solver_factory_functional (factory)) {
+			if (gnm_solver_factory_functional (factory, wbcg2)) {
+				got_it = TRUE;
 				gnm_solver_param_set_algorithm (old_params,
 								factory);
 				break;
diff --git a/src/gnm-plugin.c b/src/gnm-plugin.c
index eb2ecd9..9d42ad1 100644
--- a/src/gnm-plugin.c
+++ b/src/gnm-plugin.c
@@ -513,7 +513,8 @@ cb_load_and_create (GnmSolverFactory *factory, GnmSolverParameters *param)
 }
 
 static gboolean
-cb_load_and_functional (GnmSolverFactory *factory)
+cb_load_and_functional (GnmSolverFactory *factory,
+			WBCGtk *wbcg)
 {
 	PluginServiceSolver *ssol =
 		g_object_get_data (G_OBJECT (factory), "ssol");
@@ -529,7 +530,7 @@ cb_load_and_functional (GnmSolverFactory *factory)
 	}
 
 	functional = ssol->cbs.functional;
-	return (functional == NULL || functional (factory));
+	return (functional == NULL || functional (factory, wbcg));
 }
 
 static void
diff --git a/src/gnumeric-gconf.c b/src/gnumeric-gconf.c
index 587071f..8d2eddb 100644
--- a/src/gnumeric-gconf.c
+++ b/src/gnumeric-gconf.c
@@ -1856,6 +1856,33 @@ gnm_conf_get_functionselector_recentfunctions_node (void)
 	return get_node (watch_functionselector_recentfunctions.key);
 }
 
+static struct cb_watch_string watch_plugin_glpk_glpsol_path = {
+	0, "plugin/glpk/glpk-path", "",
+};
+
+const char *
+gnm_conf_get_plugin_glpk_glpsol_path (void)
+{
+	if (!watch_plugin_glpk_glpsol_path.handler)
+		watch_string (&watch_plugin_glpk_glpsol_path);
+	return watch_plugin_glpk_glpsol_path.var;
+}
+
+void
+gnm_conf_set_plugin_glpk_glpsol_path (const char *x)
+{
+	g_return_if_fail (x != NULL);
+	if (!watch_plugin_glpk_glpsol_path.handler)
+		watch_string (&watch_plugin_glpk_glpsol_path);
+	set_string (&watch_plugin_glpk_glpsol_path, x);
+}
+
+GOConfNode *
+gnm_conf_get_plugin_glpk_glpsol_path_node (void)
+{
+	return get_node (watch_plugin_glpk_glpsol_path.key);
+}
+
 static struct cb_watch_bool watch_plugin_latex_use_utf8 = {
 	0, "plugin/latex/use-utf8", FALSE,
 };
@@ -1882,6 +1909,33 @@ gnm_conf_get_plugin_latex_use_utf8_node (void)
 	return get_node (watch_plugin_latex_use_utf8.key);
 }
 
+static struct cb_watch_string watch_plugin_lpsolve_lpsolve_path = {
+	0, "plugin/lpsolve/lpsolve-path", "",
+};
+
+const char *
+gnm_conf_get_plugin_lpsolve_lpsolve_path (void)
+{
+	if (!watch_plugin_lpsolve_lpsolve_path.handler)
+		watch_string (&watch_plugin_lpsolve_lpsolve_path);
+	return watch_plugin_lpsolve_lpsolve_path.var;
+}
+
+void
+gnm_conf_set_plugin_lpsolve_lpsolve_path (const char *x)
+{
+	g_return_if_fail (x != NULL);
+	if (!watch_plugin_lpsolve_lpsolve_path.handler)
+		watch_string (&watch_plugin_lpsolve_lpsolve_path);
+	set_string (&watch_plugin_lpsolve_lpsolve_path, x);
+}
+
+GOConfNode *
+gnm_conf_get_plugin_lpsolve_lpsolve_path_node (void)
+{
+	return get_node (watch_plugin_lpsolve_lpsolve_path.key);
+}
+
 static struct cb_watch_bool watch_plugins_activate_new = {
 	0, "plugins/activate-new", TRUE,
 };
@@ -2874,17 +2928,10 @@ gnm_conf_get_printsetup_scale_width_node (void)
 	return get_node (watch_printsetup_scale_width.key);
 }
 
-
 static struct cb_watch_bool watch_searchreplace_change_cell_expressions = {
 	0, "searchreplace/change-cell-expressions", TRUE,
 };
 
-GOConfNode *
-gnm_conf_get_searchreplace_change_cell_expressions_node (void)
-{
-	return get_node (watch_searchreplace_change_cell_expressions.key);
-}
-
 gboolean
 gnm_conf_get_searchreplace_change_cell_expressions (void)
 {
@@ -2901,16 +2948,16 @@ gnm_conf_set_searchreplace_change_cell_expressions (gboolean x)
 	set_bool (&watch_searchreplace_change_cell_expressions, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_change_cell_other = {
-	0, "searchreplace/change-cell-other", TRUE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_change_cell_other_node (void)
+gnm_conf_get_searchreplace_change_cell_expressions_node (void)
 {
-	return get_node (watch_searchreplace_change_cell_other.key);
+	return get_node (watch_searchreplace_change_cell_expressions.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_change_cell_other = {
+	0, "searchreplace/change-cell-other", TRUE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_change_cell_other (void)
 {
@@ -2927,16 +2974,16 @@ gnm_conf_set_searchreplace_change_cell_other (gboolean x)
 	set_bool (&watch_searchreplace_change_cell_other, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_change_cell_strings = {
-	0, "searchreplace/change-cell-strings", TRUE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_change_cell_strings_node (void)
+gnm_conf_get_searchreplace_change_cell_other_node (void)
 {
-	return get_node (watch_searchreplace_change_cell_strings.key);
+	return get_node (watch_searchreplace_change_cell_other.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_change_cell_strings = {
+	0, "searchreplace/change-cell-strings", TRUE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_change_cell_strings (void)
 {
@@ -2953,16 +3000,16 @@ gnm_conf_set_searchreplace_change_cell_strings (gboolean x)
 	set_bool (&watch_searchreplace_change_cell_strings, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_change_comments = {
-	0, "searchreplace/change-comments", FALSE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_change_comments_node (void)
+gnm_conf_get_searchreplace_change_cell_strings_node (void)
 {
-	return get_node (watch_searchreplace_change_comments.key);
+	return get_node (watch_searchreplace_change_cell_strings.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_change_comments = {
+	0, "searchreplace/change-comments", FALSE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_change_comments (void)
 {
@@ -2979,16 +3026,16 @@ gnm_conf_set_searchreplace_change_comments (gboolean x)
 	set_bool (&watch_searchreplace_change_comments, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_columnmajor = {
-	0, "searchreplace/columnmajor", TRUE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_columnmajor_node (void)
+gnm_conf_get_searchreplace_change_comments_node (void)
 {
-	return get_node (watch_searchreplace_columnmajor.key);
+	return get_node (watch_searchreplace_change_comments.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_columnmajor = {
+	0, "searchreplace/columnmajor", TRUE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_columnmajor (void)
 {
@@ -3005,16 +3052,16 @@ gnm_conf_set_searchreplace_columnmajor (gboolean x)
 	set_bool (&watch_searchreplace_columnmajor, x);
 }
 
-static struct cb_watch_int watch_searchreplace_error_behaviour = {
-	0, "searchreplace/error-behaviour", 0, 4, 0,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_error_behaviour_node (void)
+gnm_conf_get_searchreplace_columnmajor_node (void)
 {
-	return get_node (watch_searchreplace_error_behaviour.key);
+	return get_node (watch_searchreplace_columnmajor.key);
 }
 
+static struct cb_watch_int watch_searchreplace_error_behaviour = {
+	0, "searchreplace/error-behaviour", 0, 4, 0,
+};
+
 int
 gnm_conf_get_searchreplace_error_behaviour (void)
 {
@@ -3031,16 +3078,16 @@ gnm_conf_set_searchreplace_error_behaviour (int x)
 	set_int (&watch_searchreplace_error_behaviour, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_ignore_case = {
-	0, "searchreplace/ignore-case", TRUE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_ignore_case_node (void)
+gnm_conf_get_searchreplace_error_behaviour_node (void)
 {
-	return get_node (watch_searchreplace_ignore_case.key);
+	return get_node (watch_searchreplace_error_behaviour.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_ignore_case = {
+	0, "searchreplace/ignore-case", TRUE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_ignore_case (void)
 {
@@ -3057,16 +3104,16 @@ gnm_conf_set_searchreplace_ignore_case (gboolean x)
 	set_bool (&watch_searchreplace_ignore_case, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_keep_strings = {
-	0, "searchreplace/keep-strings", TRUE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_keep_strings_node (void)
+gnm_conf_get_searchreplace_ignore_case_node (void)
 {
-	return get_node (watch_searchreplace_keep_strings.key);
+	return get_node (watch_searchreplace_ignore_case.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_keep_strings = {
+	0, "searchreplace/keep-strings", TRUE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_keep_strings (void)
 {
@@ -3083,16 +3130,16 @@ gnm_conf_set_searchreplace_keep_strings (gboolean x)
 	set_bool (&watch_searchreplace_keep_strings, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_preserve_case = {
-	0, "searchreplace/preserve-case", FALSE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_preserve_case_node (void)
+gnm_conf_get_searchreplace_keep_strings_node (void)
 {
-	return get_node (watch_searchreplace_preserve_case.key);
+	return get_node (watch_searchreplace_keep_strings.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_preserve_case = {
+	0, "searchreplace/preserve-case", FALSE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_preserve_case (void)
 {
@@ -3109,16 +3156,16 @@ gnm_conf_set_searchreplace_preserve_case (gboolean x)
 	set_bool (&watch_searchreplace_preserve_case, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_query = {
-	0, "searchreplace/query", FALSE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_query_node (void)
+gnm_conf_get_searchreplace_preserve_case_node (void)
 {
-	return get_node (watch_searchreplace_query.key);
+	return get_node (watch_searchreplace_preserve_case.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_query = {
+	0, "searchreplace/query", FALSE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_query (void)
 {
@@ -3135,16 +3182,16 @@ gnm_conf_set_searchreplace_query (gboolean x)
 	set_bool (&watch_searchreplace_query, x);
 }
 
-static struct cb_watch_int watch_searchreplace_regex = {
-	0, "searchreplace/regex", 0, 2, 0,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_regex_node (void)
+gnm_conf_get_searchreplace_query_node (void)
 {
-	return get_node (watch_searchreplace_regex.key);
+	return get_node (watch_searchreplace_query.key);
 }
 
+static struct cb_watch_int watch_searchreplace_regex = {
+	0, "searchreplace/regex", 0, 2, 0,
+};
+
 int
 gnm_conf_get_searchreplace_regex (void)
 {
@@ -3161,16 +3208,16 @@ gnm_conf_set_searchreplace_regex (int x)
 	set_int (&watch_searchreplace_regex, x);
 }
 
-static struct cb_watch_int watch_searchreplace_scope = {
-	0, "searchreplace/scope", 0, 2, 0,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_scope_node (void)
+gnm_conf_get_searchreplace_regex_node (void)
 {
-	return get_node (watch_searchreplace_scope.key);
+	return get_node (watch_searchreplace_regex.key);
 }
 
+static struct cb_watch_int watch_searchreplace_scope = {
+	0, "searchreplace/scope", 0, 2, 0,
+};
+
 int
 gnm_conf_get_searchreplace_scope (void)
 {
@@ -3187,16 +3234,16 @@ gnm_conf_set_searchreplace_scope (int x)
 	set_int (&watch_searchreplace_scope, x);
 }
 
-static struct cb_watch_bool watch_searchreplace_whole_words_only = {
-	0, "searchreplace/whole-words-only", FALSE,
-};
-
 GOConfNode *
-gnm_conf_get_searchreplace_whole_words_only_node (void)
+gnm_conf_get_searchreplace_scope_node (void)
 {
-	return get_node (watch_searchreplace_whole_words_only.key);
+	return get_node (watch_searchreplace_scope.key);
 }
 
+static struct cb_watch_bool watch_searchreplace_whole_words_only = {
+	0, "searchreplace/whole-words-only", FALSE,
+};
+
 gboolean
 gnm_conf_get_searchreplace_whole_words_only (void)
 {
@@ -3213,6 +3260,12 @@ gnm_conf_set_searchreplace_whole_words_only (gboolean x)
 	set_bool (&watch_searchreplace_whole_words_only, x);
 }
 
+GOConfNode *
+gnm_conf_get_searchreplace_whole_words_only_node (void)
+{
+	return get_node (watch_searchreplace_whole_words_only.key);
+}
+
 static struct cb_watch_string watch_stf_export_separator = {
 	0, "stf/export/separator", ",",
 };
@@ -3489,12 +3542,24 @@ gnm_conf_get_functionselector_dir_node (void)
 }
 
 GOConfNode *
+gnm_conf_get_plugin_glpk_dir_node (void)
+{
+	return get_node ("plugin/glpk");
+}
+
+GOConfNode *
 gnm_conf_get_plugin_latex_dir_node (void)
 {
 	return get_node ("plugin/latex");
 }
 
 GOConfNode *
+gnm_conf_get_plugin_lpsolve_dir_node (void)
+{
+	return get_node ("plugin/lpsolve");
+}
+
+GOConfNode *
 gnm_conf_get_plugins_dir_node (void)
 {
 	return get_node ("plugins");
@@ -3507,6 +3572,18 @@ gnm_conf_get_printsetup_dir_node (void)
 }
 
 GOConfNode *
+gnm_conf_get_searchreplace_dir_node (void)
+{
+	return get_node ("searchreplace");
+}
+
+GOConfNode *
+gnm_conf_get_stf_export_dir_node (void)
+{
+	return get_node ("stf/export");
+}
+
+GOConfNode *
 gnm_conf_get_undo_dir_node (void)
 {
 	return get_node ("undo");
diff --git a/src/gnumeric-gconf.h b/src/gnumeric-gconf.h
index 1ab6889..43d0c49 100644
--- a/src/gnumeric-gconf.h
+++ b/src/gnumeric-gconf.h
@@ -217,10 +217,18 @@ GOConfNode *gnm_conf_get_functionselector_recentfunctions_node (void);
 GSList *gnm_conf_get_functionselector_recentfunctions (void);
 void gnm_conf_set_functionselector_recentfunctions (GSList *);
 
+GOConfNode *gnm_conf_get_plugin_glpk_glpsol_path_node (void);
+const char *gnm_conf_get_plugin_glpk_glpsol_path (void);
+void gnm_conf_set_plugin_glpk_glpsol_path (const char *);
+
 GOConfNode *gnm_conf_get_plugin_latex_use_utf8_node (void);
 gboolean gnm_conf_get_plugin_latex_use_utf8 (void);
 void gnm_conf_set_plugin_latex_use_utf8 (gboolean);
 
+GOConfNode *gnm_conf_get_plugin_lpsolve_lpsolve_path_node (void);
+const char *gnm_conf_get_plugin_lpsolve_lpsolve_path (void);
+void gnm_conf_set_plugin_lpsolve_lpsolve_path (const char *);
+
 GOConfNode *gnm_conf_get_plugins_activate_new_node (void);
 gboolean gnm_conf_get_plugins_activate_new (void);
 void gnm_conf_set_plugins_activate_new (gboolean);
@@ -468,7 +476,9 @@ GOConfNode *gnm_conf_get_core_xml_dir_node (void);
 GOConfNode *gnm_conf_get_cut_and_paste_dir_node (void);
 GOConfNode *gnm_conf_get_dialogs_rs_dir_node (void);
 GOConfNode *gnm_conf_get_functionselector_dir_node (void);
+GOConfNode *gnm_conf_get_plugin_glpk_dir_node (void);
 GOConfNode *gnm_conf_get_plugin_latex_dir_node (void);
+GOConfNode *gnm_conf_get_plugin_lpsolve_dir_node (void);
 GOConfNode *gnm_conf_get_plugins_dir_node (void);
 GOConfNode *gnm_conf_get_printsetup_dir_node (void);
 GOConfNode *gnm_conf_get_searchreplace_dir_node (void);
diff --git a/src/ssconvert.c b/src/ssconvert.c
index bc84784..3fa2846 100644
--- a/src/ssconvert.c
+++ b/src/ssconvert.c
@@ -432,13 +432,14 @@ run_solver (Sheet *sheet, WorkbookView *wbv)
 	wb_control_set_view (wbc, wbv, NULL);
 
 	/* Pick a functional algorithm.  */
-	if (!gnm_solver_factory_functional (params->options.algorithm)) {
+	if (!gnm_solver_factory_functional (params->options.algorithm,
+					    NULL)) {
 		GSList *l;
 		for (l = gnm_solver_db_get (); l; l = l->next) {
 			GnmSolverFactory *factory = l->data;
 			if (params->options.model_type != factory->type)
 				continue;
-			if (gnm_solver_factory_functional (factory)) {
+			if (gnm_solver_factory_functional (factory, NULL)) {
 				gnm_solver_param_set_algorithm (params,
 								factory);
 				break;
diff --git a/src/tools/ChangeLog b/src/tools/ChangeLog
index 4057c54..6752682 100644
--- a/src/tools/ChangeLog
+++ b/src/tools/ChangeLog
@@ -1,3 +1,9 @@
+2010-07-03  Morten Welinder  <terra gnome org>
+
+	* gnm-solver.c (gnm_sub_solver_locate_binary): New function.
+	(gnm_solver_factory_functional): Take optional WBCGtk argument so
+	we can ask the user.  All callers changed.
+
 2010-06-28  Morten Welinder <terra gnome org>
 
 	* Release 1.10.7
@@ -21,9 +27,9 @@
 	* analysis-tools.h (analysis_tools_data_regression_t): new field
 	* analysis-tools.c (analysis_tool_regression_engine_run): use
 	  analysis_tool_get_function
-	(analysis_tool_regression_simple_engine_run): use 
-	  analysis_tool_get_function and obey the new multiple-y setting. 
-	
+	(analysis_tool_regression_simple_engine_run): use
+	  analysis_tool_get_function and obey the new multiple-y setting.
+
 2010-06-02  Morten Welinder  <terra gnome org>
 
 	* gnm-solver.c (gnm_solver_set_status, gnm_solver_elapsed):
@@ -45,7 +51,7 @@
 	* analysis-tools.h (analysis_tool_get_function): new
 	* analysis-tools.c (analysis_tool_get_function): new
 	* analysis-sign-test.h: minor formatting
-	* analysis-sign-test.c 
+	* analysis-sign-test.c
 	(analysis_tool_sign_test_two_engine_run): fix statistic
 
 2010-05-30  Morten Welinder <terra gnome org>
diff --git a/src/tools/gnm-solver.c b/src/tools/gnm-solver.c
index 031254f..3c21be3 100644
--- a/src/tools/gnm-solver.c
+++ b/src/tools/gnm-solver.c
@@ -1526,6 +1526,72 @@ gnm_sub_solver_get_cell_name (GnmSubSolver *subsol,
 	return g_hash_table_lookup (subsol->name_from_cell, (gpointer)cell);
 }
 
+char *
+gnm_sub_solver_locate_binary (const char *binary, const char *solver,
+			      const char *url,
+			      WBCGtk *wbcg)
+{
+	GtkWindow *parent;
+	GtkWidget *dialog;
+	char *path = NULL;
+	int res;
+	GtkFileChooser *fsel;
+	char *title;
+
+	parent = wbcg ? wbcg_toplevel (wbcg) : NULL;
+	dialog = gtk_message_dialog_new_with_markup
+		(parent,
+		 GTK_DIALOG_DESTROY_WITH_PARENT,
+		 GTK_MESSAGE_QUESTION,
+		 GTK_BUTTONS_YES_NO,
+		 "Gnumeric is unable to locate the program <i>%s</i> needed "
+		 "for the <i>%s</i> solver.  For more information see %s.\n\n"
+		 "Would you like to locate it yourself?",
+		 binary, solver, url);
+	title = g_strdup_printf ("Unable to locate %s", binary);
+	g_object_set (G_OBJECT (dialog),
+		      "title", title,
+		      NULL);
+	g_free (title);
+
+	res = go_gtk_dialog_run (GTK_DIALOG (dialog), parent);
+	switch (res) {
+	case GTK_RESPONSE_NO:
+	case GTK_RESPONSE_DELETE_EVENT:
+	default:
+		return NULL;
+	case GTK_RESPONSE_YES:
+		break;
+	}
+
+	title = g_strdup_printf ("Locate the %s program", binary);
+	fsel = GTK_FILE_CHOOSER
+		(g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
+			       "action", GTK_FILE_CHOOSER_ACTION_OPEN,
+			       "local-only", TRUE,
+			       "title", title,
+			       NULL));
+	g_free (title);
+	gtk_dialog_add_buttons (GTK_DIALOG (fsel),
+				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+				GTK_STOCK_EXECUTE, GTK_RESPONSE_OK,
+				NULL);
+	g_object_ref (fsel);
+	if (go_gtk_file_sel_dialog (parent, GTK_WIDGET (fsel))) {
+		path = gtk_file_chooser_get_filename (fsel);
+		if (!g_file_test (path, G_FILE_TEST_IS_EXECUTABLE)) {
+			g_free (path);
+			path = NULL;
+		}
+	}
+
+	gtk_widget_destroy (GTK_WIDGET (fsel));
+	g_object_unref (fsel);
+
+	return path;
+}
+
+
 void
 gnm_sub_solver_flush (GnmSubSolver *subsol)
 {
@@ -1619,13 +1685,14 @@ gnm_solver_factory_create (GnmSolverFactory *factory,
 }
 
 gboolean
-gnm_solver_factory_functional (GnmSolverFactory *factory)
+gnm_solver_factory_functional (GnmSolverFactory *factory,
+			       WBCGtk *wbcg)
 {
 	if (factory == NULL)
 		return FALSE;
 
 	return (factory->functional == NULL ||
-		factory->functional (factory));
+		factory->functional (factory, wbcg));
 }
 
 static int
diff --git a/src/tools/gnm-solver.h b/src/tools/gnm-solver.h
index 179d1da..70a90e7 100644
--- a/src/tools/gnm-solver.h
+++ b/src/tools/gnm-solver.h
@@ -5,6 +5,7 @@
 #include <glib-object.h>
 #include <dependent.h>
 #include <numbers.h>
+#include <wbc-gtk.h>
 
 G_BEGIN_DECLS
 
@@ -292,6 +293,10 @@ GnmCell *gnm_sub_solver_find_cell (GnmSubSolver *subsol, const char *name);
 const char *gnm_sub_solver_get_cell_name (GnmSubSolver *subsol,
 					  GnmCell const *cell);
 
+char *gnm_sub_solver_locate_binary (const char *binary, const char *solver,
+				    const char *url,
+				    WBCGtk *wbcg);
+
 /* ------------------------------------------------------------------------- */
 
 #define GNM_SOLVER_FACTORY_TYPE        (gnm_solver_factory_get_type ())
@@ -300,7 +305,8 @@ const char *gnm_sub_solver_get_cell_name (GnmSubSolver *subsol,
 
 typedef GnmSolver * (*GnmSolverCreator) (GnmSolverFactory *,
 					 GnmSolverParameters *);
-typedef gboolean (*GnmSolverFactoryFunctional) (GnmSolverFactory *);
+typedef gboolean (*GnmSolverFactoryFunctional) (GnmSolverFactory *,
+						WBCGtk *);
 
 struct GnmSolverFactory_ {
 	GObject parent;
@@ -325,7 +331,8 @@ GnmSolverFactory *gnm_solver_factory_new (const char *id,
 					  GnmSolverFactoryFunctional funct);
 GnmSolver *gnm_solver_factory_create (GnmSolverFactory *factory,
 				      GnmSolverParameters *param);
-gboolean gnm_solver_factory_functional (GnmSolverFactory *factory);
+gboolean gnm_solver_factory_functional (GnmSolverFactory *factory,
+					WBCGtk *wbcg);
 
 GSList *gnm_solver_db_get (void);
 void gnm_solver_db_register (GnmSolverFactory *factory);



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