dialog-solver.c 35 KB
Newer Older
1
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
/*
3
 * dialog-solver.c:
4 5 6
 *
 * Author:
 *  Jukka-Pekka Iivonen <iivonen@iki.fi>
7
 *
8
 * (C) Copyright 2000, 2002 by Jukka-Pekka Iivonen <iivonen@iki.fi>
9
 * (C) Copyright 2009-2013 Morten Welinder (terra@gnome.org)
jpekka's avatar
jpekka committed
10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
22
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
23 24
 */

25
#include <gnumeric-config.h>
26
#include <glib/gi18n-lib.h>
27 28
#include <gnumeric.h>
#include "dialogs.h"
29
#include "help.h"
30 31 32

#include <gui-util.h>
#include <func.h>
33
#include <dialogs/tool-dialogs.h>
34 35 36
#include <value.h>
#include <cell.h>
#include <sheet.h>
37
#include <sheet-view.h>
Morten Welinder's avatar
Morten Welinder committed
38
#include <expr.h>
39
#include <wbc-gtk.h>
40 41 42
#include <workbook.h>
#include <parse-util.h>
#include <ranges.h>
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
43
#include <commands.h>
44
#include <clipboard.h>
45
#include <selection.h>
46
#include <application.h>
47
#include <tools/gnm-solver.h>
48 49
#include <widgets/gnumeric-expr-entry.h>

50
#include <gtk/gtk.h>
51
#include <goffice/goffice.h>
52
#include <string.h>
53
#include <tools/scenarios.h>
54 55 56 57

#define SOLVER_KEY            "solver-dialog"

typedef struct {
Morten Welinder's avatar
Morten Welinder committed
58
	int                 ref_count;
59
	GtkBuilder          *gui;
60
	GtkWidget           *dialog;
61
	GtkWidget           *notebook;
62 63
	GnmExprEntry	    *target_entry;
	GnmExprEntry	    *change_cell_entry;
64 65
	GtkWidget           *max_iter_entry;
	GtkWidget           *max_time_entry;
66
	GtkWidget           *gradient_order_entry;
67 68
	GtkWidget           *solve_button;
	GtkWidget           *close_button;
Morten Welinder's avatar
Morten Welinder committed
69 70
	GtkWidget           *stop_button;
	GtkWidget           *help_button;
71 72 73
	GtkWidget           *add_button;
	GtkWidget           *change_button;
	GtkWidget           *delete_button;
74
	GtkWidget           *scenario_name_entry;
75
	struct {
Morten Welinder's avatar
Morten Welinder committed
76 77
		GnmExprEntry*entry;
		GtkWidget   *label;
78
	} lhs, rhs;
Morten Welinder's avatar
Morten Welinder committed
79 80 81
	GtkComboBox         *type_combo;
	GtkComboBox         *algorithm_combo;
	GtkTreeView         *constraint_list;
82
	GnmSolverConstraint *constr;
83 84
	GtkWidget           *warning_dialog;

85 86 87 88 89
	struct {
		GnmSolver   *solver;
		GtkWidget   *timer_widget;
		guint       timer_source;
		GtkWidget   *status_widget;
90 91
		GtkWidget   *problem_status_widget;
		GtkWidget   *objective_value_widget;
92
		guint       obj_val_source;
Morten Welinder's avatar
Morten Welinder committed
93 94
		GtkWidget   *spinner;
		guint        in_main;
95 96
	} run;

97
	Sheet		    *sheet;
Morten Welinder's avatar
Morten Welinder committed
98
	WBCGtk              *wbcg;
99 100

	GnmSolverParameters *orig_params;
101 102 103
} SolverState;


Jody Goldberg's avatar
Jody Goldberg committed
104
static char const * const problem_type_group[] = {
105 106 107
	"min_button",
	"max_button",
	"equal_to_button",
108
	NULL
109
};
110

Jody Goldberg's avatar
Jody Goldberg committed
111
static char const * const model_type_group[] = {
112 113 114
	"lp_model_button",
	"qp_model_button",
	"nlp_model_button",
115
	NULL
116 117
};

118
static void
Morten Welinder's avatar
Morten Welinder committed
119
constraint_fill (GnmSolverConstraint *c, SolverState *state)
120
{
121 122
	Sheet *sheet = state->sheet;

123 124
	c->type = gtk_combo_box_get_active (state->type_combo);

125 126 127
	gnm_solver_constraint_set_lhs
		(c,
		 gnm_expr_entry_parse_as_value (state->lhs.entry, sheet));
128

129 130 131 132 133
	gnm_solver_constraint_set_rhs
		(c,
		 gnm_solver_constraint_has_rhs (c)
		 ? gnm_expr_entry_parse_as_value (state->rhs.entry, sheet)
		 : NULL);
134
}
135

136
static gboolean
Morten Welinder's avatar
Morten Welinder committed
137
dialog_set_sec_button_sensitivity (G_GNUC_UNUSED GtkWidget *dummy,
138
				   SolverState *state)
139
{
140
	gboolean select_ready = (state->constr != NULL);
Morten Welinder's avatar
Morten Welinder committed
141
	GnmSolverConstraint *test = gnm_solver_constraint_new (NULL);
142
	gboolean ready, has_rhs;
143
	GnmSolverParameters const *param = state->sheet->solver_parameters;
144

145
	constraint_fill (test, state);
146
	ready = gnm_solver_constraint_valid (test, param);
147 148
	has_rhs = gnm_solver_constraint_has_rhs (test);
	gnm_solver_constraint_free (test);
149 150 151 152

	gtk_widget_set_sensitive (state->add_button, ready);
	gtk_widget_set_sensitive (state->change_button, select_ready && ready);
	gtk_widget_set_sensitive (state->delete_button, select_ready);
153 154
	gtk_widget_set_sensitive (GTK_WIDGET (state->rhs.entry), has_rhs);
	gtk_widget_set_sensitive (GTK_WIDGET (state->rhs.label), has_rhs);
155 156 157

	/* Return TRUE iff the current constraint is valid.  */
	return ready;
158
}
159

160
static void
161
constraint_select_click (GtkTreeSelection *Selection,
Morten Welinder's avatar
Morten Welinder committed
162
			 SolverState * state)
163
{
164 165
	GtkTreeModel *store;
	GtkTreeIter iter;
Morten Welinder's avatar
Morten Welinder committed
166
	GnmSolverConstraint const *c;
167
	GnmValue const *lhs, *rhs;
168

169 170 171 172
	if (gtk_tree_selection_get_selected (Selection, &store, &iter))
		gtk_tree_model_get (store, &iter, 1, &state->constr, -1);
	else
		state->constr = NULL;
173
	dialog_set_sec_button_sensitivity (NULL, state);
174

175
	if (state->constr == NULL)
176
		return; /* Fail? */
Morten Welinder's avatar
Morten Welinder committed
177 178
	c = state->constr;

179 180
	lhs = gnm_solver_constraint_get_lhs (c);
	if (lhs) {
Morten Welinder's avatar
Morten Welinder committed
181
		GnmExprTop const *texpr =
182
			gnm_expr_top_new_constant (value_dup (lhs));
Morten Welinder's avatar
Morten Welinder committed
183 184 185 186 187 188 189 190 191 192
		GnmParsePos pp;

		gnm_expr_entry_load_from_expr
			(state->lhs.entry,
			 texpr,
			 parse_pos_init_sheet (&pp, state->sheet));
		gnm_expr_top_unref (texpr);
	} else
		gnm_expr_entry_load_from_text (state->lhs.entry, "");

193 194
	rhs = gnm_solver_constraint_get_rhs (c);
	if (rhs && gnm_solver_constraint_has_rhs (c)) {
Morten Welinder's avatar
Morten Welinder committed
195
		GnmExprTop const *texpr =
196
			gnm_expr_top_new_constant (value_dup (rhs));
Morten Welinder's avatar
Morten Welinder committed
197 198 199 200 201 202 203
		GnmParsePos pp;

		gnm_expr_entry_load_from_expr
			(state->rhs.entry,
			 texpr,
			 parse_pos_init_sheet (&pp, state->sheet));
		gnm_expr_top_unref (texpr);
jpekka's avatar
jpekka committed
204
	} else
205
		gnm_expr_entry_load_from_text (state->rhs.entry, "");
206

Morten Welinder's avatar
Morten Welinder committed
207
	gtk_combo_box_set_active (state->type_combo, c->type);
208 209
}

210 211 212 213 214
/**
 * cb_dialog_delete_clicked:
 * @button:
 * @state:
 *
215
 *
216 217
 **/
static void
Morten Welinder's avatar
Morten Welinder committed
218
cb_dialog_delete_clicked (G_GNUC_UNUSED GtkWidget *button, SolverState *state)
219
{
220
	if (state->constr != NULL) {
221 222
		GtkTreeIter iter;
		GtkTreeModel *store;
223
		GnmSolverParameters *param = state->sheet->solver_parameters;
224

225 226 227
		param->constraints =
			g_slist_remove (param->constraints, state->constr);
		gnm_solver_constraint_free (state->constr);
228
		state->constr = NULL;
229 230 231 232 233 234

		if (gtk_tree_selection_get_selected (
			    gtk_tree_view_get_selection (state->constraint_list),
			    &store, &iter))
			gtk_list_store_remove ((GtkListStore*)store, &iter);
	}
235
}
236

237
static void
238
constraint_fill_row (SolverState *state, GtkListStore *store, GtkTreeIter *iter)
239
{
240
	char         *text;
Morten Welinder's avatar
Morten Welinder committed
241
	GnmSolverConstraint *c = state->constr;
242

243 244
	constraint_fill (c, state);

245
	text = gnm_solver_constraint_as_str (c, state->sheet);
246
	gtk_list_store_set (store, iter, 0, text, 1, c, -1);
247 248 249 250 251
	g_free (text);
	gtk_tree_selection_select_iter (gtk_tree_view_get_selection (state->constraint_list), iter);
}

static void
252
cb_dialog_add_clicked (SolverState *state)
253
{
254 255 256
	if (dialog_set_sec_button_sensitivity (NULL, state)) {
		GtkTreeIter   iter;
		GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (state->constraint_list));
257
		GnmSolverParameters *param = state->sheet->solver_parameters;
258 259

		gtk_list_store_append (store, &iter);
260
		state->constr = gnm_solver_constraint_new (state->sheet);
261
		constraint_fill_row (state, store, &iter);
262 263
		param->constraints =
			g_slist_append (param->constraints, state->constr);
264
	}
265 266 267
}

static void
Morten Welinder's avatar
Morten Welinder committed
268
cb_dialog_change_clicked (GtkWidget *button, SolverState *state)
269
{
270
	if (state->constr != NULL) {
271 272 273 274 275 276
		GtkTreeIter iter;
		GtkTreeModel *store;

		if (gtk_tree_selection_get_selected (
			    gtk_tree_view_get_selection (state->constraint_list),
			    &store, &iter))
277
			constraint_fill_row (state, (GtkListStore *)store, &iter);
278
	}
279
}
280

281
static void
Morten Welinder's avatar
Morten Welinder committed
282
dialog_set_main_button_sensitivity (G_GNUC_UNUSED GtkWidget *dummy,
283
				    SolverState *state)
284
{
285
	gboolean ready;
286

287 288 289 290
	ready = gnm_expr_entry_is_cell_ref (state->target_entry, state->sheet,
					    FALSE)
		&& gnm_expr_entry_is_cell_ref (state->change_cell_entry,
					       state->sheet, TRUE);
291
	gtk_widget_set_sensitive (state->solve_button, ready);
292
}
293

294
static gboolean
295
fill_algorithm_combo (SolverState *state, GnmSolverModelType type)
296 297 298 299
{
	GtkListStore *store =
		gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
	GSList *solvers, *l;
300 301
	int sel = 0, i;
	GnmSolverParameters *param =state->sheet->solver_parameters;
302 303 304 305 306 307 308 309 310 311

	gtk_combo_box_set_model (state->algorithm_combo, GTK_TREE_MODEL (store));

	l = NULL;
	for (solvers = gnm_solver_db_get (); solvers; solvers = solvers->next) {
		GnmSolverFactory *entry = solvers->data;
		if (type != entry->type)
			continue;
		l = g_slist_prepend (l, entry);
	}
312
	solvers = g_slist_reverse (l);
313 314 315 316 317 318

	gtk_widget_set_sensitive (GTK_WIDGET (state->solve_button),
				  solvers != NULL);
	if (!solvers)
		return FALSE;

319
	for (l = solvers, i = 0; l; l = l->next, i++) {
320 321 322
		GnmSolverFactory *factory = l->data;
		GtkTreeIter iter;

323 324 325
		if (param->options.algorithm == factory)
			sel = i;

326 327 328 329 330 331 332 333
		gtk_list_store_append (store, &iter);
		gtk_list_store_set (store, &iter,
				    0, factory->name,
				    1, factory,
				    -1);
	}
	g_slist_free (solvers);

334
	gtk_combo_box_set_active (state->algorithm_combo, sel);
335

Morten Welinder's avatar
Morten Welinder committed
336 337
	g_object_unref (store);

338 339 340
	return TRUE;
}

341
static void
Morten Welinder's avatar
Morten Welinder committed
342
cb_dialog_model_type_clicked (G_GNUC_UNUSED GtkWidget *button,
343
			      SolverState *state)
344
{
345
	GnmSolverModelType type;
346
	gboolean any;
347

348
	type = gnm_gui_group_value (state->gui, model_type_group);
349 350 351 352 353 354 355 356 357 358
	any = fill_algorithm_combo (state, type);

	if (!any) {
		go_gtk_notice_nonmodal_dialog
			(GTK_WINDOW (state->dialog),
			 &(state->warning_dialog),
			 GTK_MESSAGE_INFO,
			 _("Looking for a subject for your thesis? "
			   "Maybe you would like to write a solver for "
			   "Gnumeric?"));
359
	}
360 361
}

362
static void
Morten Welinder's avatar
Morten Welinder committed
363
unref_state (SolverState *state)
364
{
Morten Welinder's avatar
Morten Welinder committed
365 366 367 368
	state->ref_count--;
	if (state->ref_count > 0)
		return;

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
	if (state->orig_params)
		g_object_unref (state->orig_params);
	g_free (state);
}

static GOUndo *
set_params (Sheet *sheet, GnmSolverParameters *params)
{
	return go_undo_binary_new
		(sheet, g_object_ref (params),
		 (GOUndoBinaryFunc)gnm_sheet_set_solver_params,
		 NULL, g_object_unref);
}

#define GET_BOOL_ENTRY(name_, field_)					\
384
	do {								\
Morten Welinder's avatar
Morten Welinder committed
385
		GtkWidget *w_ = go_gtk_builder_get_widget (state->gui, (name_)); \
386 387
		param->field_ = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w_)); \
	} while (0)
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410

static void
extract_settings (SolverState *state)
{
	GnmSolverParameters *param = state->sheet->solver_parameters;
	GtkTreeIter iter;
	GnmValue *target_range;
	GnmValue *input_range;
	GnmSolverFactory *factory = NULL;

	target_range = gnm_expr_entry_parse_as_value (state->target_entry,
						      state->sheet);
	input_range = gnm_expr_entry_parse_as_value (state->change_cell_entry,
						     state->sheet);

	gnm_solver_param_set_input (param, input_range);

	gnm_solver_param_set_target (param,
				     target_range
				     ? &target_range->v_range.cell.a
				     : NULL);

	param->problem_type =
411
		gnm_gui_group_value (state->gui, problem_type_group);
412
	param->options.model_type =
413
		gnm_gui_group_value (state->gui, model_type_group);
414

415 416 417 418 419 420
	if (gtk_combo_box_get_active_iter (state->algorithm_combo, &iter)) {
		gtk_tree_model_get (gtk_combo_box_get_model (state->algorithm_combo),
				    &iter, 1, &factory, -1);
		gnm_solver_param_set_algorithm (param, factory);
	} else
		gnm_solver_param_set_algorithm (param, NULL);
421 422 423 424 425

	param->options.max_iter = gtk_spin_button_get_value
		(GTK_SPIN_BUTTON (state->max_iter_entry));
	param->options.max_time_sec = gtk_spin_button_get_value
		(GTK_SPIN_BUTTON (state->max_time_entry));
426 427
	param->options.gradient_order = gtk_spin_button_get_value
		(GTK_SPIN_BUTTON (state->gradient_order_entry));
428

429
	GET_BOOL_ENTRY ("autoscale_button", options.automatic_scaling);
430 431 432
	GET_BOOL_ENTRY ("non_neg_button", options.assume_non_negative);
	GET_BOOL_ENTRY ("all_int_button", options.assume_discrete);
	GET_BOOL_ENTRY ("program", options.program_report);
433
	GET_BOOL_ENTRY ("sensitivity", options.sensitivity_report);
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454

	g_free (param->options.scenario_name);
	param->options.scenario_name = g_strdup
		(gtk_entry_get_text (GTK_ENTRY (state->scenario_name_entry)));

	GET_BOOL_ENTRY ("optimal_scenario", options.add_scenario);

	value_release (target_range);
}

#undef GET_BOOL_ENTRY

static void
check_for_changed_options (SolverState *state)
{
	Sheet *sheet = state->sheet;

	if (!gnm_solver_param_equal (sheet->solver_parameters,
				     state->orig_params)) {
		GOUndo *undo = set_params (sheet, state->orig_params);
		GOUndo *redo = set_params (sheet, sheet->solver_parameters);
Morten Welinder's avatar
Morten Welinder committed
455
		cmd_generic (GNM_WBC (state->wbcg),
456 457
			     _("Changing solver parameters"),
			     undo, redo);
458 459 460 461 462 463 464 465

		g_object_unref (state->orig_params);
		state->orig_params =
			gnm_solver_param_dup (sheet->solver_parameters,
					      sheet);
	}
}

466 467
static void
cb_dialog_solver_destroy (SolverState *state)
468
{
469
	g_return_if_fail (state != NULL);
470

Morten Welinder's avatar
Morten Welinder committed
471 472 473 474 475
	if (state->run.solver) {
		gnm_solver_stop (state->run.solver, NULL);
		g_object_set (state->run.solver, "result", NULL, NULL);
	}

476 477 478 479
	extract_settings (state);

	check_for_changed_options (state);

480
	if (state->gui != NULL) {
481
		g_object_unref (state->gui);
482
		state->gui = NULL;
483 484
	}

485
	wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
486

487
	state->dialog = NULL;
488 489
}

490
static void
Morten Welinder's avatar
Morten Welinder committed
491
cb_dialog_close_clicked (G_GNUC_UNUSED GtkWidget *button,
492
			 SolverState *state)
493
{
494
	gtk_widget_destroy (state->dialog);
495 496
}

497 498 499 500 501 502 503 504 505 506 507
static void
cb_stop_solver (SolverState *state)
{
	GnmSolver *sol = state->run.solver;

	switch (sol->status) {
	case GNM_SOLVER_STATUS_RUNNING: {
		gboolean ok = gnm_solver_stop (sol, NULL);
		if (!ok) {
			g_warning ("Failed to stop solver!");
		}
508
		g_object_set (sol, "result", NULL, NULL);
509 510 511 512 513 514 515 516
		break;
	}

	default:
		break;
	}
}

Morten Welinder's avatar
Morten Welinder committed
517 518 519 520 521 522 523 524 525
static void
remove_timer_source (SolverState *state)
{
	if (state->run.timer_source) {
		g_source_remove (state->run.timer_source);
		state->run.timer_source = 0;
	}
}

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
static void
remove_objective_value_source (SolverState *state)
{
	if (state->run.obj_val_source) {
		g_source_remove (state->run.obj_val_source);
		state->run.obj_val_source = 0;
	}
}

static void
update_obj_value (SolverState *state)
{
	GnmSolver *sol = state->run.solver;
	GnmSolverResult *r = sol->result;
	char *valtxt;
	const char *txt;

	switch (r ? r->quality : GNM_SOLVER_RESULT_NONE) {
	default:
	case GNM_SOLVER_RESULT_NONE:
		txt = "";
		break;

	case GNM_SOLVER_RESULT_FEASIBLE:
		txt = _("Feasible");
		break;

	case GNM_SOLVER_RESULT_OPTIMAL:
		txt = _("Optimal");
		break;

	case GNM_SOLVER_RESULT_INFEASIBLE:
		txt = _("Infeasible");
		break;

	case GNM_SOLVER_RESULT_UNBOUNDED:
		txt = _("Unbounded");
		break;
	}
	gtk_label_set_text (GTK_LABEL (state->run.problem_status_widget), txt);

	if (gnm_solver_has_solution (sol)) {
		txt = valtxt = gnm_format_value (go_format_general (),
						 r->value);
	} else {
		valtxt = NULL;
Morten Welinder's avatar
Morten Welinder committed
572
		txt = "";
573 574 575 576 577 578 579 580 581
	}

	gtk_label_set_text (GTK_LABEL (state->run.objective_value_widget),
			    txt);
	g_free (valtxt);

	remove_objective_value_source (state);
}

582 583 584 585 586 587
static void
cb_notify_status (SolverState *state)
{
	GnmSolver *sol = state->run.solver;
	const char *text;
	gboolean finished = gnm_solver_finished (sol);
Morten Welinder's avatar
Morten Welinder committed
588
	gboolean running = FALSE;
589 590 591 592 593 594 595 596 597 598 599 600 601

	switch (sol->status) {
	case GNM_SOLVER_STATUS_READY:
		text = _("Ready");
		break;
	case GNM_SOLVER_STATUS_PREPARING:
		text = _("Preparing");
		break;
	case GNM_SOLVER_STATUS_PREPARED:
		text = _("Prepared");
		break;
	case GNM_SOLVER_STATUS_RUNNING:
		text = _("Running");
Morten Welinder's avatar
Morten Welinder committed
602
		running = TRUE;
603 604 605 606 607 608 609 610 611 612 613 614 615
		break;
	case GNM_SOLVER_STATUS_DONE:
		text = _("Done");
		break;
	default:
	case GNM_SOLVER_STATUS_ERROR:
		text = _("Error");
		break;
	case GNM_SOLVER_STATUS_CANCELLED:
		text = _("Cancelled");
		break;
	}

616
	if (sol->reason) {
617
		char *text2 = g_strconcat (text,
618
					   " (", sol->reason, ")",
619 620 621 622 623 624 625
					   NULL);
		gtk_label_set_text (GTK_LABEL (state->run.status_widget),
				    text2);
		g_free (text2);
	} else {
		gtk_label_set_text (GTK_LABEL (state->run.status_widget), text);
	}
626

Morten Welinder's avatar
Morten Welinder committed
627 628 629 630 631
	gtk_widget_set_visible (state->run.spinner, running);
	gtk_widget_set_visible (state->stop_button, !finished);
	gtk_widget_set_sensitive (state->solve_button, finished);
	gtk_widget_set_sensitive (state->close_button, finished);

632 633 634
	if (state->run.obj_val_source)
		update_obj_value (state);

635
	if (finished) {
Morten Welinder's avatar
Morten Welinder committed
636 637 638
		remove_timer_source (state);
		if (state->run.in_main)
			gtk_main_quit ();
639 640 641
	}
}

642 643 644 645 646 647 648 649
static gboolean
cb_obj_val_tick (SolverState *state)
{
	state->run.obj_val_source = 0;
	update_obj_value (state);
	return FALSE;
}

650 651 652
static void
cb_notify_result (SolverState *state)
{
653 654 655
	if (state->run.obj_val_source == 0)
		state->run.obj_val_source = g_timeout_add
			(100, (GSourceFunc)cb_obj_val_tick, state);
656 657 658 659 660
}

static gboolean
cb_timer_tick (SolverState *state)
{
661 662
	GnmSolver *sol = state->run.solver;
	double dsecs = gnm_solver_elapsed (sol);
663
	int secs = (int)CLAMP (dsecs, 0, INT_MAX);
664 665 666 667 668 669 670 671 672 673
	int hh = secs / 3600;
	int mm = secs / 60 % 60;
	int ss = secs % 60;
	char *txt = hh
		? g_strdup_printf ("%02d:%02d:%02d", hh, mm, ss)
		: g_strdup_printf ("%02d:%02d", mm, ss);

	gtk_label_set_text (GTK_LABEL (state->run.timer_widget), txt);
	g_free (txt);

674
	if (gnm_solver_check_timeout (sol)) {
675 676 677
		cb_notify_status (state);
	}

678 679 680
	return TRUE;
}

681 682 683 684
static void
create_report (GnmSolver *sol, SolverState *state)
{
	Sheet *sheet = state->sheet;
685
	char *base = g_strdup_printf (_("%s %%s Report"), sheet->name_unquoted);
686 687 688 689 690
	gnm_solver_create_report (sol, base);
	g_free (base);
}


691
static GnmSolverResult *
692
run_solver (SolverState *state, GnmSolverParameters *param)
693 694 695 696 697 698 699 700 701
{
	GError *err = NULL;
	gboolean ok;
	GnmSheetRange sr;
	GOUndo *undo = NULL;
	GnmSolver *sol = NULL;
	GnmValue const *vinput;
	GtkWindow *top = GTK_WINDOW (gtk_widget_get_toplevel (state->dialog));
	GnmSolverResult *res = NULL;
Morten Welinder's avatar
Morten Welinder committed
702 703

	state->ref_count++;
704

705 706
	sol = gnm_solver_factory_functional (param->options.algorithm,
					     state->wbcg)
707 708
		? gnm_solver_factory_create (param->options.algorithm, param)
		: NULL;
709 710
	if (!sol) {
		go_gtk_notice_dialog (top, GTK_MESSAGE_ERROR,
711
				      _("The chosen solver is not functional."));
712 713 714
		goto fail;
	}

715 716
	gtk_notebook_set_current_page (GTK_NOTEBOOK (state->notebook), -1);

717 718 719 720 721 722 723
	state->run.solver = sol;

	vinput = gnm_solver_param_get_input (param);
	gnm_sheet_range_from_value (&sr, vinput);
	if (!sr.sheet) sr.sheet = param->sheet;
	undo = clipboard_copy_range_undo (sr.sheet, &sr.range);

Morten Welinder's avatar
Morten Welinder committed
724 725 726 727 728 729 730
	g_signal_connect_swapped (G_OBJECT (sol),
				  "notify::status",
				  G_CALLBACK (cb_notify_status),
				  state);
	g_signal_connect_swapped (G_OBJECT (sol),
				  "notify::reason",
				  G_CALLBACK (cb_notify_status),
731 732 733
				  state);
	cb_notify_status (state);

Morten Welinder's avatar
Morten Welinder committed
734 735 736 737
	g_signal_connect_swapped (G_OBJECT (sol),
				  "notify::result",
				  G_CALLBACK (cb_notify_result),
				  state);
738 739 740 741 742 743 744 745 746
	cb_notify_result (state);

	state->run.timer_source = g_timeout_add_seconds
		(1, (GSourceFunc)cb_timer_tick, state);
	cb_timer_tick (state);

	/* ---------------------------------------- */

	ok = gnm_solver_start (sol,
Morten Welinder's avatar
Morten Welinder committed
747
			       GNM_WBC (state->wbcg),
748 749
			       &err);
	if (ok) {
Morten Welinder's avatar
Morten Welinder committed
750 751 752 753 754 755
		state->run.in_main++;
		go_cmd_context_set_sensitive (GO_CMD_CONTEXT (state->wbcg), FALSE);
		gtk_main ();
		go_cmd_context_set_sensitive (GO_CMD_CONTEXT (state->wbcg), TRUE);
		state->run.in_main--;
		ok = gnm_solver_has_solution (sol);
756 757
	} else if (err) {
		gnm_solver_set_reason (sol, err->message);
758
	}
759
	g_clear_error (&err);
760

761
	remove_objective_value_source (state);
Morten Welinder's avatar
Morten Welinder committed
762
	remove_timer_source (state);
763

764 765
	/* ---------------------------------------- */

Morten Welinder's avatar
Morten Welinder committed
766
	if (ok) {
767 768 769
		GOUndo *redo;

		gnm_solver_store_result (sol);
770 771
		redo = clipboard_copy_range_undo (sr.sheet, &sr.range);

772 773
		if (param->options.program_report ||
		    param->options.sensitivity_report) {
774 775 776 777 778 779 780 781 782 783 784
			Workbook *wb = param->sheet->workbook;
			GOUndo *undo_report, *redo_report;

			undo_report = go_undo_binary_new
				(wb,
				 workbook_sheet_state_new (wb),
				 (GOUndoBinaryFunc)workbook_sheet_state_restore,
				 NULL,
				 (GFreeFunc)workbook_sheet_state_free);
			undo = go_undo_combine (undo, undo_report);

785 786
			create_report (sol, state);

787 788 789 790 791 792 793 794 795
			redo_report = go_undo_binary_new
				(wb,
				 workbook_sheet_state_new (wb),
				 (GOUndoBinaryFunc)workbook_sheet_state_restore,
				 NULL,
				 (GFreeFunc)workbook_sheet_state_free);
			redo = go_undo_combine (redo, redo_report);
		}

Morten Welinder's avatar
Morten Welinder committed
796
		cmd_generic (GNM_WBC (state->wbcg),
797 798
			     _("Running solver"),
			     undo, redo);
799 800 801 802 803 804 805 806 807 808 809 810 811
		res = g_object_ref (sol->result);
		undo = redo = NULL;
	}

fail:
	if (undo)
		g_object_unref (undo);

	if (state->run.solver) {
		g_object_unref (state->run.solver);
		state->run.solver = NULL;
	}

Morten Welinder's avatar
Morten Welinder committed
812 813
	unref_state (state);

814 815 816 817
	return res;
}


818
static void
819
solver_add_scenario (SolverState *state, GnmSolverResult *res, gchar const *name)
820
{
821
	GnmSolverParameters *param = state->sheet->solver_parameters;
822 823 824
	GnmValue const *vinput;
	GnmScenario *sc;
	GnmSheetRange sr;
Morten Welinder's avatar
Morten Welinder committed
825
	WorkbookControl *wbc = GNM_WBC (state->wbcg);
826

827 828 829 830
	vinput = gnm_solver_param_get_input (param);
	gnm_sheet_range_from_value (&sr, vinput);

	sc = gnm_sheet_scenario_new (param->sheet, name);
831 832 833 834 835 836 837 838 839 840 841 842
	switch (res->quality) {
	case GNM_SOLVER_RESULT_OPTIMAL:
		gnm_scenario_set_comment
			(sc, _("Optimal solution created by solver.\n"));
		break;
	case GNM_SOLVER_RESULT_FEASIBLE:
		gnm_scenario_set_comment
			(sc, _("Feasible solution created by solver.\n"));
		break;
	default:
		break;
	}
Morten Welinder's avatar
Morten Welinder committed
843
	gnm_scenario_add_area (sc, &sr);
844

845
	cmd_scenario_add (wbc, sc, sc->sheet);
846
}
Morten Welinder's avatar
Morten Welinder committed
847

848 849 850 851 852
/**
 * cb_dialog_solve_clicked:
 * @button:
 * @state:
 *
853
 *
854 855
 **/
static void
Morten Welinder's avatar
Morten Welinder committed
856
cb_dialog_solve_clicked (G_GNUC_UNUSED GtkWidget *button,
857
			 SolverState *state)
858
{
859 860
	GnmSolverResult *res;
	GnmSolverParameters *param = state->sheet->solver_parameters;
Morten Welinder's avatar
Morten Welinder committed
861
	GError *err = NULL;
862

863
	if (state->warning_dialog != NULL) {
864
		gtk_widget_destroy (state->warning_dialog);
865 866
		state->warning_dialog = NULL;
	}
867

868
	extract_settings (state);
869

870 871 872 873
	if (!gnm_solver_param_valid (param, &err)) {
		GtkWidget *top = gtk_widget_get_toplevel (state->dialog);
		go_gtk_notice_dialog (GTK_WINDOW (top), GTK_MESSAGE_ERROR,
				      "%s", err->message);
874 875 876
		goto out;
	}

877 878
	check_for_changed_options (state);

879 880
	res = run_solver (state, param);

881
	gnm_app_recalc ();
882

Jody Goldberg's avatar
Jody Goldberg committed
883
	if (res != NULL) {
884 885
		if ((res->quality == GNM_SOLVER_RESULT_OPTIMAL ||
		     res->quality == GNM_SOLVER_RESULT_FEASIBLE) &&
886
		    param->options.add_scenario)
887
			solver_add_scenario (state, res,
888
					     param->options.scenario_name);
889

890 891
		g_object_unref (res);
	} else if (err) {
Morten Welinder's avatar
Morten Welinder committed
892 893 894 895
		go_gtk_notice_nonmodal_dialog
			(GTK_WINDOW (state->dialog),
			 &(state->warning_dialog),
			 GTK_MESSAGE_ERROR,
896
			 "%s", err->message);
897
	}
Morten Welinder's avatar
Morten Welinder committed
898

899
 out:
Morten Welinder's avatar
Morten Welinder committed
900 901
	if (err)
		g_error_free (err);
902
}
903

904 905
#define INIT_BOOL_ENTRY(name_, field_)					\
do {									\
Morten Welinder's avatar
Morten Welinder committed
906
	GtkWidget *w_ = go_gtk_builder_get_widget (state->gui, (name_));	\
907 908 909 910
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w_),		\
				      param->field_);			\
} while (0)

911

912
/**
913
 * dialog_solver_init:
914 915 916 917 918 919
 * @state:
 *
 * Create the dialog (guru).
 *
 **/
static gboolean
920
dialog_solver_init (SolverState *state)
921
{
922
	GtkGrid *grid;
923
	GnmSolverParameters *param;
924 925 926
	GtkCellRenderer *renderer;
	GtkListStore *store;
	GtkTreeViewColumn *column;
927
	GSList *cl;
928 929
	GnmCell *target_cell;
	GnmValue const *input;
930
	int i;
931

932
	param = state->sheet->solver_parameters;
933

934
	state->gui = gnm_gtk_builder_load ("res:ui/solver.ui", NULL, GO_CMD_CONTEXT (state->wbcg));
935 936 937
        if (state->gui == NULL)
                return TRUE;

Morten Welinder's avatar
Morten Welinder committed
938
	state->dialog = go_gtk_builder_get_widget (state->gui, "Solver");
939 940 941
        if (state->dialog == NULL)
                return TRUE;

942 943
	state->notebook = go_gtk_builder_get_widget (state->gui, "solver_notebook");

944
	/*  buttons  */
Morten Welinder's avatar
Morten Welinder committed
945
	state->solve_button  = go_gtk_builder_get_widget (state->gui, "solvebutton");
jpekka's avatar
jpekka committed
946 947
	g_signal_connect (G_OBJECT (state->solve_button), "clicked",
			  G_CALLBACK (cb_dialog_solve_clicked), state);
948

Morten Welinder's avatar
Morten Welinder committed
949
	state->close_button  = go_gtk_builder_get_widget (state->gui, "closebutton");
jpekka's avatar
jpekka committed
950 951
	g_signal_connect (G_OBJECT (state->close_button), "clicked",
			  G_CALLBACK (cb_dialog_close_clicked), state);
952

Morten Welinder's avatar
Morten Welinder committed
953 954
	state->help_button = go_gtk_builder_get_widget (state->gui, "helpbutton");
	gnm_init_help_button (state->help_button, GNUMERIC_HELP_LINK_SOLVER);
955

Morten Welinder's avatar
Morten Welinder committed
956
	state->add_button  = go_gtk_builder_get_widget (state->gui, "addbutton");
957
	gtk_button_set_alignment (GTK_BUTTON (state->add_button), 0.5, .5);
958 959
	g_signal_connect_swapped (G_OBJECT (state->add_button), "clicked",
		G_CALLBACK (cb_dialog_add_clicked), state);
960

Morten Welinder's avatar
Morten Welinder committed
961
	state->change_button = go_gtk_builder_get_widget (state->gui,
jpekka's avatar
jpekka committed
962 963 964
						     "changebutton");
	g_signal_connect (G_OBJECT (state->change_button), "clicked",
			  G_CALLBACK (cb_dialog_change_clicked), state);
965

Morten Welinder's avatar
Morten Welinder committed
966
	state->delete_button = go_gtk_builder_get_widget (state->gui,
jpekka's avatar
jpekka committed
967
						     "deletebutton");
968
	gtk_button_set_alignment (GTK_BUTTON (state->delete_button), 0.5, .5);
jpekka's avatar
jpekka committed
969 970
	g_signal_connect (G_OBJECT (state->delete_button), "clicked",
			  G_CALLBACK (cb_dialog_delete_clicked), state);
971

Morten Welinder's avatar
Morten Welinder committed
972 973 974 975 976
	state->stop_button = go_gtk_builder_get_widget (state->gui, "stopbutton");
	g_signal_connect_swapped (G_OBJECT (state->stop_button),
				  "clicked", G_CALLBACK (cb_stop_solver),
				  state);

Morten Welinder's avatar
Morten Welinder committed
977
	/* target_entry */
978 979
	grid = GTK_GRID (go_gtk_builder_get_widget (state->gui,
						 "parameter-grid"));
980
	state->target_entry = gnm_expr_entry_new (state->wbcg, TRUE);
981
	gnm_expr_entry_set_flags (state->target_entry,
982
		GNM_EE_SINGLE_RANGE |
983
		GNM_EE_FORCE_ABS_REF |
984
		GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK);
985
	gtk_widget_set_hexpand (GTK_WIDGET (state->target_entry), TRUE);
986
	gtk_grid_attach (grid, GTK_WIDGET (state->target_entry), 1, 0, 2, 1);
Morten Welinder's avatar
Morten Welinder committed
987
	gnm_editable_enters (GTK_WINDOW (state->dialog),
988
				  GTK_WIDGET (state->target_entry));
989
	gtk_widget_show (GTK_WIDGET (state->target_entry));
jpekka's avatar
jpekka committed
990 991 992
	g_signal_connect_after (G_OBJECT (state->target_entry),	"changed",
			G_CALLBACK (dialog_set_main_button_sensitivity),
				state);
993

Morten Welinder's avatar
Morten Welinder committed
994
	/* change_cell_entry */
995
	state->change_cell_entry = gnm_expr_entry_new (state->wbcg, TRUE);
996
	gnm_expr_entry_set_flags (state->change_cell_entry,
997
		GNM_EE_SINGLE_RANGE |
998
		GNM_EE_FORCE_ABS_REF |
999
		GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK);
1000 1001
	gtk_widget_set_hexpand (GTK_WIDGET (state->change_cell_entry), TRUE);
	gtk_grid_attach (grid,
1002
	                 GTK_WIDGET (state->change_cell_entry), 1, 2, 2, 1);
Morten Welinder's avatar
Morten Welinder committed
1003
	gnm_editable_enters (GTK_WINDOW (state->dialog),
1004
				  GTK_WIDGET (state->change_cell_entry));
1005
	gtk_widget_show (GTK_WIDGET (state->change_cell_entry));
jpekka's avatar
jpekka committed
1006
	g_signal_connect_after (G_OBJECT (state->change_cell_entry), "changed",
1007
		G_CALLBACK (dialog_set_main_button_sensitivity), state);
1008

1009
	/* Algorithm */
1010
	state->algorithm_combo = GTK_COMBO_BOX
Morten Welinder's avatar
Morten Welinder committed
1011
		(go_gtk_builder_get_widget (state->gui, "algorithm_combo"));
1012 1013
	renderer = (GtkCellRenderer*) gtk_cell_renderer_text_new();
	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state->algorithm_combo), renderer, TRUE);
1014 1015 1016 1017
	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (state->algorithm_combo), renderer,
					"text", 0,
					NULL);
	fill_algorithm_combo (state, param->options.model_type);
1018

1019 1020
	for (i = 0; model_type_group[i]; i++) {
		const char *bname = model_type_group[i];
Morten Welinder's avatar
Morten Welinder committed
1021
		GtkWidget *w = go_gtk_builder_get_widget(state->gui, bname);
1022 1023 1024
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
					      param->options.model_type ==
					      (GnmSolverModelType)i);
1025 1026 1027
		g_signal_connect (G_OBJECT (w), "clicked",
				  G_CALLBACK (cb_dialog_model_type_clicked), state);
	}
1028

1029
	/* Options */
Morten Welinder's avatar
Morten Welinder committed
1030
	state->max_iter_entry = go_gtk_builder_get_widget (state->gui,
jpekka's avatar
jpekka committed
1031
						      "max_iter_entry");
1032 1033
	gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max_iter_entry),
				   param->options.max_iter);
1034

Morten Welinder's avatar
Morten Welinder committed
1035
	state->max_time_entry = go_gtk_builder_get_widget (state->gui,
jpekka's avatar
jpekka committed
1036
						      "max_time_entry");
1037 1038
	gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->max_time_entry),
				   param->options.max_time_sec);
1039

1040 1041 1042 1043 1044
	state->gradient_order_entry = go_gtk_builder_get_widget (state->gui,
								 "gradient_order_entry");
	gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->gradient_order_entry),
				   param->options.gradient_order);

1045
	/* lhs_entry */
1046 1047
	grid = GTK_GRID (go_gtk_builder_get_widget (state->gui,
	                                            "constraints-grid"));
1048 1049
	state->lhs.entry = gnm_expr_entry_new (state->wbcg, TRUE);
	gnm_expr_entry_set_flags (state->lhs.entry,
1050
		GNM_EE_SINGLE_RANGE |
1051
		GNM_EE_FORCE_ABS_REF |
1052
		GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK);
1053 1054
	gtk_widget_set_hexpand (GTK_WIDGET (state->lhs.entry), TRUE);
	gtk_grid_attach (grid, GTK_WIDGET (state->lhs.entry), 0, 4, 1, 1);
Morten Welinder's avatar
Morten Welinder committed
1055
	state->lhs.label = go_gtk_builder_get_widget (state->gui, "lhs_label");
1056 1057 1058 1059
	gtk_label_set_mnemonic_widget (GTK_LABEL (state->lhs.label),
		GTK_WIDGET (state->lhs.entry));
	gtk_widget_show (GTK_WIDGET (state->lhs.entry));
	g_signal_connect_after (G_OBJECT (state->lhs.entry),
1060 1061
		"changed",
		G_CALLBACK (dialog_set_sec_button_sensitivity), state);
1062 1063 1064
	g_signal_connect_swapped (
		gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->lhs.entry)),
		"activate", G_CALLBACK (cb_dialog_add_clicked), state);
1065

1066
	/* rhs_entry */
1067 1068
	state->rhs.entry = gnm_expr_entry_new (state->wbcg, TRUE);
	gnm_expr_entry_set_flags (state->rhs.entry,
1069 1070 1071 1072 1073
				  GNM_EE_SINGLE_RANGE |
				  GNM_EE_FORCE_ABS_REF |
				  GNM_EE_SHEET_OPTIONAL |
				  GNM_EE_CONSTANT_ALLOWED,
				  GNM_EE_MASK);
1074 1075
	gtk_widget_set_hexpand (GTK_WIDGET (state->rhs.entry), TRUE);
	gtk_grid_attach (grid, GTK_WIDGET (state->rhs.entry), 2, 4, 1, 1);
1076
	gtk_widget_show (GTK_WIDGET (state->rhs.entry));
Morten Welinder's avatar
Morten Welinder committed
1077
	state->rhs.label = go_gtk_builder_get_widget (state->gui, "rhs_label");
1078 1079 1080
	gtk_label_set_mnemonic_widget (
		GTK_LABEL (state->rhs.label), GTK_WIDGET (state->rhs.entry));
	g_signal_connect_after (G_OBJECT (state->rhs.entry),
1081 1082
		"changed",
		G_CALLBACK (dialog_set_sec_button_sensitivity), state);
1083 1084 1085
	g_signal_connect_swapped (
		gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->rhs.entry)),
		"activate", G_CALLBACK (cb_dialog_add_clicked), state);
1086

1087
	/* type_menu */
1088
	state->type_combo = GTK_COMBO_BOX
Morten Welinder's avatar
Morten Welinder committed
1089
		(go_gtk_builder_get_widget (state->gui, "type_menu"));
1090 1091
	gtk_combo_box_set_active (state->type_combo, 0);
	g_signal_connect (state->type_combo, "changed",
jpekka's avatar
jpekka committed
1092 1093
			  G_CALLBACK (dialog_set_sec_button_sensitivity),
			  state);
1094

1095
	/* constraint_list */
Morten Welinder's avatar
Morten Welinder committed
1096
	state->constraint_list = GTK_TREE_VIEW (go_gtk_builder_get_widget
1097
					    (state->gui, "constraint_list"));
1098

1099 1100
	state->constr = NULL;
	g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (state->constraint_list)), "changed",
jpekka's avatar
jpekka committed
1101
			  G_CALLBACK (constraint_select_click), state);
1102
	gtk_tree_view_set_reorderable (state->constraint_list, TRUE);
1103 1104
	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
	gtk_tree_view_set_model (state->constraint_list, GTK_TREE_MODEL(store));
1105
	renderer = gtk_cell_renderer_text_new ();
1106
	column = gtk_tree_view_column_new_with_attributes (
1107 1108
			_("Subject to the Constraints:"),
			renderer, "text", 0, NULL);
1109 1110
	gtk_tree_view_column_set_expand (column, TRUE);
	gtk_tree_view_append_column (state->constraint_list, column);
1111

1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
	{
		GtkWidget *w = GTK_WIDGET (state->constraint_list);
		int width, height, vsep;
		PangoLayout *layout =
			gtk_widget_create_pango_layout (w, "Mg19");

		gtk_widget_style_get (w,
				      "vertical_separator", &vsep,
				      NULL);

		pango_layout_get_pixel_size (layout, &width, &height);
		gtk_widget_set_size_request (w,
					     -1,
					     (2 * height + vsep) * (4 + 1));
		g_object_unref (layout);
	}

jpekka's avatar
jpekka committed
1129
/* Loading the old solver specs... from param  */
1130

1131
	for (cl = param->constraints; cl; cl = cl->next) {
Morten Welinder's avatar
Morten Welinder committed
1132
		GnmSolverConstraint const *c = cl->data;
1133 1134 1135 1136
		GtkTreeIter iter;
		char *str;

		gtk_list_store_append (store, &iter);
1137
		str = gnm_solver_constraint_as_str (c, state->sheet);
1138 1139 1140
		gtk_list_store_set (store, &iter, 0, str, 1, c, -1);
		g_free (str);
	}
Morten Welinder's avatar
Morten Welinder committed
1141
	g_object_unref (store);
1142

1143
	INIT_BOOL_ENTRY ("autoscale_button", options.automatic_scaling);
1144 1145 1146
	INIT_BOOL_ENTRY ("non_neg_button", options.assume_non_negative);
	INIT_BOOL_ENTRY ("all_int_button", options.assume_discrete);
	INIT_BOOL_ENTRY ("program", options.program_report);
1147
	INIT_BOOL_ENTRY ("sensitivity", options.sensitivity_report);
1148

1149 1150
	input = gnm_solver_param_get_input (param);
	if (input != NULL)
1151
		gnm_expr_entry_load_from_text (state->change_cell_entry,
1152 1153 1154
					       value_peek_string (input));
	target_cell = gnm_solver_param_get_target_cell (param);
	if (target_cell)
1155
		gnm_expr_entry_load_from_text (state->target_entry,
1156
					       cell_name (target_cell));
1157
	else {
Morten Welinder's avatar
Morten Welinder committed
1158
		SheetView *sv = wb_control_cur_sheet_view
Morten Welinder's avatar
Morten Welinder committed
1159
			(GNM_WBC (state->wbcg));
1160 1161
		if (sv) {
			GnmRange first = {sv->edit_pos, sv->edit_pos};
1162
			gnm_expr_entry_load_from_range (state->target_entry,
1163
							state->sheet, &first);
1164 1165
		}
	}
1166

1167
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
Morten Welinder's avatar
Morten Welinder committed
1168
		go_gtk_builder_get_widget(state->gui, "max_button")),
1169
			param->problem_type == GNM_SOLVER_MAXIMIZE);
1170
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
Morten Welinder's avatar
Morten Welinder committed
1171
		go_gtk_builder_get_widget(state->gui, "min_button")),
1172
			param->problem_type == GNM_SOLVER_MINIMIZE);
1173
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
Morten Welinder's avatar
Morten Welinder committed
1174
		go_gtk_builder_get_widget(state->gui, "no_scenario")),
1175 1176
			! param->options.add_scenario);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
Morten Welinder's avatar
Morten Welinder committed
1177
		go_gtk_builder_get_widget(state->gui, "optimal_scenario")),
1178 1179
			param->options.add_scenario);

Morten Welinder's avatar
Morten Welinder committed
1180
	state->scenario_name_entry = go_gtk_builder_get_widget
1181
		(state->gui, "scenario_name_entry");
Morten Welinder's avatar
Morten Welinder committed
1182
	gtk_entry_set_text (GTK_ENTRY (state->scenario_name_entry),
1183
			    param->options.scenario_name);
1184

1185 1186 1187 1188
	state->run.status_widget = go_gtk_builder_get_widget (state->gui, "solver_status_label");
	state->run.problem_status_widget = go_gtk_builder_get_widget (state->gui, "problem_status_label");
	state->run.objective_value_widget = go_gtk_builder_get_widget (state->gui, "objective_value_label");
	state->run.timer_widget = go_gtk_builder_get_widget (state->gui, "elapsed_time_label");
Morten Welinder's avatar
Morten Welinder committed
1189 1190
	state->run.spinner = go_gtk_builder_get_widget (state->gui, "run_spinner");

1191

1192
/* Done */
1193
	gnm_expr_entry_grab_focus (state->target_entry, FALSE);
1194
	wbcg_set_entry (state->wbcg, state->target_entry);
1195 1196 1197 1198

	dialog_set_main_button_sensitivity (NULL, state);
	dialog_set_sec_button_sensitivity (NULL, state);