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

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

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

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

#define SOLVER_KEY            "solver-dialog"

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

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

95
	Sheet		    *sheet;
Morten Welinder's avatar
Morten Welinder committed
96
	WBCGtk              *wbcg;
97 98

	GnmSolverParameters *orig_params;
99 100 101
} SolverState;


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

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

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

121 122
	c->type = gtk_combo_box_get_active (state->type_combo);

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

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

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

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

	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);
151 152
	gtk_widget_set_sensitive (GTK_WIDGET (state->rhs.entry), has_rhs);
	gtk_widget_set_sensitive (GTK_WIDGET (state->rhs.label), has_rhs);
153 154 155

	/* Return TRUE iff the current constraint is valid.  */
	return ready;
156
}
157

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

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

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

177 178
	lhs = gnm_solver_constraint_get_lhs (c);
	if (lhs) {
Morten Welinder's avatar
Morten Welinder committed
179
		GnmExprTop const *texpr =
180
			gnm_expr_top_new_constant (value_dup (lhs));
Morten Welinder's avatar
Morten Welinder committed
181 182 183 184 185 186 187 188 189 190
		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, "");

191 192
	rhs = gnm_solver_constraint_get_rhs (c);
	if (rhs && gnm_solver_constraint_has_rhs (c)) {
Morten Welinder's avatar
Morten Welinder committed
193
		GnmExprTop const *texpr =
194
			gnm_expr_top_new_constant (value_dup (rhs));
Morten Welinder's avatar
Morten Welinder committed
195 196 197 198 199 200 201
		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
202
	} else
203
		gnm_expr_entry_load_from_text (state->rhs.entry, "");
204

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

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

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

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

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

241 242
	constraint_fill (c, state);

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

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

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

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

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

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

285 286 287 288
	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);
289
	gtk_widget_set_sensitive (state->solve_button, ready);
290
}
291

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

	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);
	}
310
	solvers = g_slist_reverse (l);
311 312 313 314 315 316

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

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

321 322 323
		if (param->options.algorithm == factory)
			sel = i;

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

332
	gtk_combo_box_set_active (state->algorithm_combo, sel);
333

Morten Welinder's avatar
Morten Welinder committed
334 335
	g_object_unref (store);

336 337 338
	return TRUE;
}

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

346
	type = gnm_gui_group_value (state->gui, model_type_group);
347 348 349 350 351 352 353 354 355 356
	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?"));
357
	}
358 359
}

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

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
	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_)					\
382
	do {								\
Morten Welinder's avatar
Morten Welinder committed
383
		GtkWidget *w_ = go_gtk_builder_get_widget (state->gui, (name_)); \
384 385
		param->field_ = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w_)); \
	} while (0)
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408

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 =
409
		gnm_gui_group_value (state->gui, problem_type_group);
410
	param->options.model_type =
411
		gnm_gui_group_value (state->gui, model_type_group);
412

413 414 415 416 417 418
	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);
419 420 421 422 423

	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));
424 425
	param->options.gradient_order = gtk_spin_button_get_value
		(GTK_SPIN_BUTTON (state->gradient_order_entry));
426

427
	GET_BOOL_ENTRY ("autoscale_button", options.automatic_scaling);
428 429 430
	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);
431
	GET_BOOL_ENTRY ("sensitivity", options.sensitivity_report);
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452

	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
453
		cmd_generic (GNM_WBC (state->wbcg),
454 455
			     _("Changing solver parameters"),
			     undo, redo);
456 457 458 459 460 461 462 463

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

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

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

474 475 476 477
	extract_settings (state);

	check_for_changed_options (state);

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

483
	wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
484

485
	state->dialog = NULL;
486 487
}

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

495 496 497 498 499 500 501 502 503 504 505
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!");
		}
506
		g_object_set (sol, "result", NULL, NULL);
507 508 509 510 511 512 513 514
		break;
	}

	default:
		break;
	}
}

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

524 525 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
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
570
		txt = "";
571 572 573 574 575 576 577 578 579
	}

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

	remove_objective_value_source (state);
}

580 581 582 583 584 585
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
586
	gboolean running = FALSE;
587 588 589 590 591 592 593 594 595 596 597 598 599

	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
600
		running = TRUE;
601 602 603 604 605 606 607 608 609 610 611 612 613
		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;
	}

614
	if (sol->reason) {
615
		char *text2 = g_strconcat (text,
616
					   " (", sol->reason, ")",
617 618 619 620 621 622 623
					   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);
	}
624

Morten Welinder's avatar
Morten Welinder committed
625 626 627 628 629
	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);

630 631 632
	if (state->run.obj_val_source)
		update_obj_value (state);

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

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

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

static gboolean
cb_timer_tick (SolverState *state)
{
659 660
	GnmSolver *sol = state->run.solver;
	double dsecs = gnm_solver_elapsed (sol);
661
	int secs = (int)CLAMP (dsecs, 0, INT_MAX);
662 663 664 665 666 667 668 669 670 671
	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);

672
	if (gnm_solver_check_timeout (sol)) {
673 674 675
		cb_notify_status (state);
	}

676 677 678
	return TRUE;
}

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


689
static GnmSolverResult *
690
run_solver (SolverState *state, GnmSolverParameters *param)
691 692 693 694 695 696 697 698 699
{
	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
700 701

	state->ref_count++;
702

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

713 714
	gtk_notebook_set_current_page (GTK_NOTEBOOK (state->notebook), -1);

715 716 717 718 719 720 721
	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
722 723 724 725 726 727 728
	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),
729 730 731
				  state);
	cb_notify_status (state);

Morten Welinder's avatar
Morten Welinder committed
732 733 734 735
	g_signal_connect_swapped (G_OBJECT (sol),
				  "notify::result",
				  G_CALLBACK (cb_notify_result),
				  state);
736 737 738 739 740 741 742 743 744
	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
745
			       GNM_WBC (state->wbcg),
746 747
			       &err);
	if (ok) {
Morten Welinder's avatar
Morten Welinder committed
748 749 750 751 752 753
		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);
754 755
	} else if (err) {
		gnm_solver_set_reason (sol, err->message);
756
	}
757
	g_clear_error (&err);
758

759
	remove_objective_value_source (state);
Morten Welinder's avatar
Morten Welinder committed
760
	remove_timer_source (state);
761

762 763
	/* ---------------------------------------- */

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

		gnm_solver_store_result (sol);
768 769
		redo = clipboard_copy_range_undo (sr.sheet, &sr.range);

770 771
		if (param->options.program_report ||
		    param->options.sensitivity_report) {
772 773 774 775 776 777 778 779 780 781 782
			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);

783 784
			create_report (sol, state);

785 786 787 788 789 790 791 792 793
			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
794
		cmd_generic (GNM_WBC (state->wbcg),
795 796
			     _("Running solver"),
			     undo, redo);
797 798 799 800 801 802 803 804 805 806 807 808 809
		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
810 811
	unref_state (state);

812 813 814 815
	return res;
}


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

825 826 827 828
	vinput = gnm_solver_param_get_input (param);
	gnm_sheet_range_from_value (&sr, vinput);

	sc = gnm_sheet_scenario_new (param->sheet, name);
829 830 831 832 833 834 835 836 837 838 839 840
	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
841
	gnm_scenario_add_area (sc, &sr);
842

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

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

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

866
	extract_settings (state);
867

868 869 870 871
	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);
872 873 874
		goto out;
	}

875 876
	check_for_changed_options (state);

877 878
	res = run_solver (state, param);

879
	gnm_app_recalc ();
880

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

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

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

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

909

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

930
	param = state->sheet->solver_parameters;
931

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

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

940 941
	state->notebook = go_gtk_builder_get_widget (state->gui, "solver_notebook");

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

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

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

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

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

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

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

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

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

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

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

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

1038 1039 1040 1041 1042
	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);

1043
	/* lhs_entry */
1044 1045
	grid = GTK_GRID (go_gtk_builder_get_widget (state->gui,
	                                            "constraints-grid"));
1046 1047
	state->lhs.entry = gnm_expr_entry_new (state->wbcg, TRUE);
	gnm_expr_entry_set_flags (state->lhs.entry,
1048
		GNM_EE_SINGLE_RANGE |
1049
		GNM_EE_FORCE_ABS_REF |
1050
		GNM_EE_SHEET_OPTIONAL, GNM_EE_MASK);
1051 1052
	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
1053
	state->lhs.label = go_gtk_builder_get_widget (state->gui, "lhs_label");
1054 1055 1056 1057
	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),
1058 1059
		"changed",
		G_CALLBACK (dialog_set_sec_button_sensitivity), state);
1060 1061 1062
	g_signal_connect_swapped (
		gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (state->lhs.entry)),
		"activate", G_CALLBACK (cb_dialog_add_clicked), state);
1063

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

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

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

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

1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
	{
		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
1127
/* Loading the old solver specs... from param  */
1128

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

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

1141
	INIT_BOOL_ENTRY ("autoscale_button", options.automatic_scaling);
1142 1143 1144
	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);
1145
	INIT_BOOL_ENTRY ("sensitivity", options.sensitivity_report);
1146

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

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

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

1183 1184 1185 1186
	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
1187 1188
	state->run.spinner = go_gtk_builder_get_widget (state->gui, "run_spinner");

1189

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

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

1197 1198
/* dialog */
	wbc_gtk_attach_guru (state->wbcg, state->dialog);