1 /*
2  * dialog-analysis-tool-kaplan-meier.c:
3  *
4  * Authors:
5   *  Andreas J. Guelzow  <aguelzow@taliesin.ca>
6  *
7  * (C) Copyright 2008 by Andreas J. Guelzow  <aguelzow@pyrshep.ca>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see <https://www.gnu.org/licenses/>.
21  */
22 
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
25 #include <gnumeric.h>
26 #include <dialogs/dialogs.h>
27 #include <tools/analysis-kaplan-meier.h>
28 #include <tools/analysis-tools.h>
29 
30 #include <workbook.h>
31 #include <workbook-control.h>
32 #include <wbc-gtk.h>
33 #include <workbook-view.h>
34 #include <gui-util.h>
35 #include <parse-util.h>
36 #include <gnm-format.h>
37 #include <dialogs/tool-dialogs.h>
38 #include <dialogs/dao-gui-utils.h>
39 #include <sheet.h>
40 #include <expr.h>
41 #include <number-match.h>
42 #include <ranges.h>
43 #include <selection.h>
44 #include <value.h>
45 #include <commands.h>
46 #include <dialogs/help.h>
47 
48 #include <widgets/gnm-dao.h>
49 #include <widgets/gnm-expr-entry.h>
50 
51 #include <string.h>
52 
53 #define KAPLAN_MEIER_KEY      "analysistools-kaplan-meier-dialog"
54 
55 typedef struct {
56 	GnmGenericToolState base;
57 	GtkWidget *censorship_button;
58 	GtkWidget *censor_spin_from;
59 	GtkWidget *censor_spin_to;
60 	GtkWidget *graph_button;
61 	GtkWidget *logrank_button;
62 	GtkWidget *tick_button;
63 	GtkWidget *add_group_button;
64 	GtkWidget *remove_group_button;
65 	GtkWidget *std_error_button;
66 	GtkWidget *groups_check;
67 	GtkWidget *groups_grid;
68 	GnmExprEntry *groups_input;
69 	GtkTreeView *groups_treeview;
70 	GtkListStore *groups_list;
71 } KaplanMeierToolState;
72 
73 enum
74 {
75      GROUP_NAME,
76      GROUP_FROM,
77      GROUP_TO,
78      GROUP_ADJUSTMENT_FROM,
79      GROUP_ADJUSTMENT_TO,
80      GROUP_COLUMNS
81 };
82 
83 
84 /**
85  * kaplan_meier_tool_update_sensitivity_cb:
86  * @dummy:
87  * @state:
88  *
89  * Update the dialog widgets sensitivity
90  **/
91 static void
kaplan_meier_tool_update_sensitivity_cb(G_GNUC_UNUSED GtkWidget * dummy,KaplanMeierToolState * state)92 kaplan_meier_tool_update_sensitivity_cb (G_GNUC_UNUSED GtkWidget *dummy,
93 				      KaplanMeierToolState *state)
94 {
95 	gboolean censorship;
96 	gboolean groups;
97         GnmValue *input_range;
98         GnmValue *input_range_2 = NULL;
99 	int height, width;
100 
101 	censorship = gtk_toggle_button_get_active (
102 		GTK_TOGGLE_BUTTON (state->censorship_button));
103 	groups = gtk_toggle_button_get_active (
104 		GTK_TOGGLE_BUTTON (state->groups_check));
105 
106 	gtk_widget_set_sensitive (state->tick_button, censorship);
107 
108 	input_range = gnm_expr_entry_parse_as_value
109 		(GNM_EXPR_ENTRY (state->base.input_entry), state->base.sheet);
110 	if (input_range == NULL) {
111 		gtk_label_set_text (GTK_LABEL (state->base.warning),
112 				    _("The time column is not valid."));
113 		gtk_widget_set_sensitive (state->base.ok_button, FALSE);
114 		return;
115 	}
116 
117 	height = input_range->v_range.cell.b.row - input_range->v_range.cell.a.row;
118 	width  = input_range->v_range.cell.b.col - input_range->v_range.cell.a.col;
119 
120 	value_release (input_range);
121 
122 	if (width != 0) {
123 		gtk_label_set_text (GTK_LABEL (state->base.warning),
124 				    _("The time column should be part of a single column."));
125 		gtk_widget_set_sensitive (state->base.ok_button, FALSE);
126 		return;
127 	}
128 
129 	if (censorship) {
130 		input_range_2 =  gnm_expr_entry_parse_as_value
131 			(GNM_EXPR_ENTRY (state->base.input_entry_2), state->base.sheet);
132 		if (input_range_2 == NULL) {
133 			gtk_label_set_text (GTK_LABEL (state->base.warning),
134 					    _("The censorship column is not valid."));
135 			gtk_widget_set_sensitive (state->base.ok_button, FALSE);
136 			return;
137 		}
138 
139 		if (input_range_2->v_range.cell.b.col != input_range_2->v_range.cell.a.col) {
140 			gtk_label_set_text (GTK_LABEL (state->base.warning),
141 					    _("The censorship column should be part of a single column."));
142 			gtk_widget_set_sensitive (state->base.ok_button, FALSE);
143 			value_release (input_range_2);
144 			return;
145 		}
146 		if (input_range_2->v_range.cell.b.row - input_range_2->v_range.cell.a.row != height) {
147 			gtk_label_set_text (GTK_LABEL (state->base.warning),
148 					    _("The censorship and time columns should have the same height."));
149 			gtk_widget_set_sensitive (state->base.ok_button, FALSE);
150 			value_release (input_range_2);
151 			return;
152 		}
153 
154 		value_release (input_range_2);
155 	}
156 
157 	if (groups) {
158 		input_range_2 =  gnm_expr_entry_parse_as_value
159 			(GNM_EXPR_ENTRY (state->groups_input), state->base.sheet);
160 
161 		if (input_range_2 == NULL) {
162 			gtk_label_set_text (GTK_LABEL (state->base.warning),
163 					    _("The groups column is not valid."));
164 			gtk_widget_set_sensitive (state->base.ok_button, FALSE);
165 			return;
166 		}
167 		if (input_range_2->v_range.cell.b.col != input_range_2->v_range.cell.a.col) {
168 			gtk_label_set_text (GTK_LABEL (state->base.warning),
169 					    _("The groups column should be part of a single column."));
170 			gtk_widget_set_sensitive (state->base.ok_button, FALSE);
171 			value_release (input_range_2);
172 			return;
173 		}
174 		if (input_range_2->v_range.cell.b.row - input_range_2->v_range.cell.a.row != height) {
175 			gtk_label_set_text (GTK_LABEL (state->base.warning),
176 					    _("The groups and time columns should have the same height."));
177 			gtk_widget_set_sensitive (state->base.ok_button, FALSE);
178 			value_release (input_range_2);
179 			return;
180 		}
181 
182 		value_release (input_range_2);
183 	}
184 
185         if (!gnm_dao_is_ready (GNM_DAO (state->base.gdao))) {
186 		gtk_label_set_text (GTK_LABEL (state->base.warning),
187 				    _("The output specification "
188 				      "is invalid."));
189 		gtk_widget_set_sensitive (state->base.ok_button, FALSE);
190 		return;
191 	}
192 
193 	gtk_label_set_text (GTK_LABEL (state->base.warning), "");
194 	gtk_widget_set_sensitive (state->base.ok_button, TRUE);
195 
196 	return;
197 }
198 
199 static gboolean
kaplan_meier_tool_get_groups_cb(GtkTreeModel * model,G_GNUC_UNUSED GtkTreePath * path,GtkTreeIter * iter,gpointer data)200 kaplan_meier_tool_get_groups_cb (GtkTreeModel *model,
201 				 G_GNUC_UNUSED GtkTreePath *path,
202 				 GtkTreeIter *iter,
203 				 gpointer data)
204 {
205 	GSList **list = data;
206 	analysis_tools_kaplan_meier_group_t *group_item =
207 		g_new0 (analysis_tools_kaplan_meier_group_t, 1);
208 
209 	gtk_tree_model_get (model, iter,
210 			    GROUP_NAME, &(group_item->name),
211 			    GROUP_FROM, &(group_item->group_from),
212 			    GROUP_TO, &(group_item->group_to),
213 			    -1);
214 	*list = g_slist_prepend (*list, group_item);
215 
216 	return FALSE;
217 }
218 
219 static GSList *
kaplan_meier_tool_get_groups(KaplanMeierToolState * state)220 kaplan_meier_tool_get_groups (KaplanMeierToolState *state)
221 {
222 	GSList *list = NULL;
223 
224 	if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (state->groups_check)))
225 		return NULL;
226 
227 	gtk_tree_model_foreach (GTK_TREE_MODEL (state->groups_list),
228 				kaplan_meier_tool_get_groups_cb,
229 				&list);
230 	return g_slist_reverse (list);
231 }
232 
233 /**
234  * kaplan_meier_tool_ok_clicked_cb:
235  * @button:
236  * @state:
237  *
238  * Retrieve the information from the dialog and call the kaplan_meier_tool.
239  * Note that we assume that the ok_button is only active if the entry fields
240  * contain sensible data.
241  **/
242 static void
kaplan_meier_tool_ok_clicked_cb(G_GNUC_UNUSED GtkWidget * button,KaplanMeierToolState * state)243 kaplan_meier_tool_ok_clicked_cb (G_GNUC_UNUSED GtkWidget *button,
244 			      KaplanMeierToolState *state)
245 {
246 	data_analysis_output_t  *dao;
247 	analysis_tools_data_kaplan_meier_t  *data;
248 
249 	data = g_new0 (analysis_tools_data_kaplan_meier_t, 1);
250 	dao  = parse_output ((GnmGenericToolState *)state, NULL);
251 
252 
253 	data->base.wbc = GNM_WBC (state->base.wbcg);
254 
255 	if (state->base.warning_dialog != NULL)
256 		gtk_widget_destroy (state->base.warning_dialog);
257 
258 	data->base.range_1 = gnm_expr_entry_parse_as_value
259 		(GNM_EXPR_ENTRY (state->base.input_entry), state->base.sheet);
260 
261 	data->censored = gtk_toggle_button_get_active (
262 		GTK_TOGGLE_BUTTON (state->censorship_button));
263 
264 	if (data->censored)
265 		data->base.range_2 =  gnm_expr_entry_parse_as_value
266 			(GNM_EXPR_ENTRY (state->base.input_entry_2), state->base.sheet);
267 	else
268 		data->base.range_2 = NULL;
269 
270 	data->censor_mark = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (state->censor_spin_from));
271 	data->censor_mark_to = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (state->censor_spin_to));
272 
273 	data->group_list = kaplan_meier_tool_get_groups (state);
274 	if (data->group_list == NULL) {
275 		data->range_3 = NULL;
276 		data->logrank_test = FALSE;
277 	} else {
278 		data->range_3 = gnm_expr_entry_parse_as_value
279 			(GNM_EXPR_ENTRY (state->groups_input), state->base.sheet);
280 		data->logrank_test = gtk_toggle_button_get_active (
281 			GTK_TOGGLE_BUTTON (state->logrank_button));
282 	}
283 
284 	data->median = gtk_toggle_button_get_active (
285 		GTK_TOGGLE_BUTTON (go_gtk_builder_get_widget
286 				   (state->base.gui,
287 				    "median-button")));
288 	data->chart = gtk_toggle_button_get_active (
289 		GTK_TOGGLE_BUTTON (state->graph_button));
290 	data->ticks = gtk_toggle_button_get_active (
291 		GTK_TOGGLE_BUTTON (state->tick_button));
292 	data->std_err = gtk_toggle_button_get_active (
293 		GTK_TOGGLE_BUTTON (state->std_error_button));
294 
295 	if (!cmd_analysis_tool (GNM_WBC (state->base.wbcg),
296 				state->base.sheet,
297 				dao, data, analysis_tool_kaplan_meier_engine,
298 				TRUE))
299 		gtk_widget_destroy (state->base.dialog);
300 
301 	return;
302 }
303 
304 /**
305  * kaplan_meier_tool_set_graph:
306  * @widget:
307  * @focus_widget:
308  * @state:
309  *
310  *
311  **/
312 static gboolean
kaplan_meier_tool_set_graph_cb(G_GNUC_UNUSED GtkWidget * dummy,KaplanMeierToolState * state)313 kaplan_meier_tool_set_graph_cb (G_GNUC_UNUSED GtkWidget *dummy,
314 				KaplanMeierToolState *state)
315 {
316 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->graph_button), TRUE);
317 	kaplan_meier_tool_update_sensitivity_cb (NULL, state);
318 	return FALSE;
319 }
320 
321 /**
322  * kaplan_meier_tool_set_censorship:
323  * @widget:
324  * @event:
325  * @state:
326  *
327  **/
328 static gboolean
kaplan_meier_tool_set_censorship_cb(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEventFocus * event,KaplanMeierToolState * state)329 kaplan_meier_tool_set_censorship_cb (G_GNUC_UNUSED GtkWidget *widget,
330 				     G_GNUC_UNUSED GdkEventFocus *event,
331 				     KaplanMeierToolState *state)
332 {
333 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->censorship_button), TRUE);
334 	return FALSE;
335 }
336 
337 static gboolean
kaplan_meier_tool_set_groups_cb(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEventFocus * event,KaplanMeierToolState * state)338 kaplan_meier_tool_set_groups_cb (G_GNUC_UNUSED GtkWidget *widget,
339 				 G_GNUC_UNUSED GdkEventFocus *event,
340 				 KaplanMeierToolState *state)
341 {
342 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->groups_check), TRUE);
343 	return FALSE;
344 }
345 
346 
347 static gboolean
kaplan_meier_tool_set_censor_from_cb(G_GNUC_UNUSED GtkWidget * dummy,KaplanMeierToolState * state)348 kaplan_meier_tool_set_censor_from_cb (G_GNUC_UNUSED GtkWidget *dummy,
349 				KaplanMeierToolState *state)
350 {
351 	gtk_spin_button_set_range (GTK_SPIN_BUTTON (state->censor_spin_to),
352 				   gtk_spin_button_get_value (GTK_SPIN_BUTTON (state->censor_spin_from)),G_MAXSHORT);
353 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->censorship_button), TRUE);
354 
355 	return FALSE;
356 }
357 static gboolean
kaplan_meier_tool_set_censor_cb(G_GNUC_UNUSED GtkWidget * dummy,KaplanMeierToolState * state)358 kaplan_meier_tool_set_censor_cb (G_GNUC_UNUSED GtkWidget *dummy,
359 				KaplanMeierToolState *state)
360 {
361 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->censorship_button), TRUE);
362 
363 	return FALSE;
364 }
365 
366 static void
cb_group_name_edited(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,KaplanMeierToolState * state)367 cb_group_name_edited (GtkCellRendererText *cell,
368 		      gchar               *path_string,
369 		      gchar               *new_text,
370 		      KaplanMeierToolState *state)
371 {
372 	GtkTreeIter iter;
373 	GtkTreePath *path;
374 
375 	if (cell != NULL) {
376 		path = gtk_tree_path_new_from_string (path_string);
377 		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (state->groups_list),
378 					     &iter, path))
379 			gtk_list_store_set (state->groups_list, &iter,
380 					    GROUP_NAME, new_text, -1);
381 		else
382 			g_warning ("Did not get a valid iterator");
383 		gtk_tree_path_free (path);
384 	}
385 }
386 
387 static void
cb_change_to(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,KaplanMeierToolState * state)388 cb_change_to (GtkCellRendererText *cell,
389 	      gchar               *path_string,
390 	      gchar               *new_text,
391 	      KaplanMeierToolState *state)
392 {
393 	GtkTreeIter iter;
394 	GtkTreePath *path;
395 	guint val = (guint) (atoi (new_text));
396 
397 	if (cell != NULL) {
398 		path = gtk_tree_path_new_from_string (path_string);
399 		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (state->groups_list),
400 					     &iter, path))
401 			gtk_list_store_set (state->groups_list, &iter,
402 					    GROUP_TO, val, -1);
403 		else
404 			g_warning ("Did not get a valid iterator");
405 		gtk_tree_path_free (path);
406 	}
407 }
408 
409 static void
cb_change_from(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,KaplanMeierToolState * state)410 cb_change_from (GtkCellRendererText *cell,
411 	      gchar               *path_string,
412 	      gchar               *new_text,
413 	      KaplanMeierToolState *state)
414 {
415 	if (cell != NULL) {
416 		GtkTreeIter iter;
417 		GtkTreePath *path;
418 		guint val = (guint) (atoi (new_text));
419 		guint old_to;
420 		GObject *adjustment_to;
421 
422 
423 		path = gtk_tree_path_new_from_string (path_string);
424 		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (state->groups_list),
425 					     &iter, path))
426 			gtk_list_store_set (state->groups_list, &iter,
427 					    GROUP_FROM, val,
428 					    -1);
429 		else
430 			g_warning ("Did not get a valid iterator");
431 		gtk_tree_path_free (path);
432 
433 		gtk_tree_model_get (GTK_TREE_MODEL (state->groups_list), &iter,
434 				    GROUP_TO, &old_to,
435 				    GROUP_ADJUSTMENT_TO, &adjustment_to,
436 				    -1);
437 
438 		if (old_to < val)
439 			gtk_list_store_set (state->groups_list, &iter,
440 					    GROUP_TO, val,
441 					    -1);
442 		g_object_set (adjustment_to, "lower", (gdouble) val, NULL);
443 
444 	}
445 }
446 
447 static void
cb_selection_changed(GtkTreeSelection * selection,KaplanMeierToolState * state)448 cb_selection_changed (GtkTreeSelection *selection,
449 		      KaplanMeierToolState *state)
450 {
451 	gtk_widget_set_sensitive (state->remove_group_button,
452 				  gtk_tree_selection_get_selected (selection, NULL, NULL));
453 }
454 
455 static void
kaplan_meier_tool_update_groups_sensitivity_cb(G_GNUC_UNUSED GtkWidget * dummy,KaplanMeierToolState * state)456 kaplan_meier_tool_update_groups_sensitivity_cb (G_GNUC_UNUSED GtkWidget *dummy,
457 				      KaplanMeierToolState *state)
458 {
459 	gboolean groups = gtk_toggle_button_get_active (
460 		GTK_TOGGLE_BUTTON (state->groups_check));
461 	GtkTreeSelection  *selection = gtk_tree_view_get_selection (state->groups_treeview);
462 
463 	gtk_widget_set_sensitive (state->add_group_button, groups);
464 	gtk_widget_set_sensitive (GTK_WIDGET (state->groups_treeview), groups);
465 
466 	if (groups) {
467 		cb_selection_changed (selection, state);
468 		gtk_widget_set_sensitive (state->logrank_button, TRUE);
469 	} else {
470 		gtk_tree_selection_unselect_all (selection);
471 		gtk_widget_set_sensitive (state->remove_group_button, FALSE);
472 		gtk_widget_set_sensitive (state->logrank_button, FALSE);
473 	}
474 }
475 
476 static void
dialog_kaplan_meier_tool_treeview_add_item(KaplanMeierToolState * state,guint i)477 dialog_kaplan_meier_tool_treeview_add_item  (KaplanMeierToolState *state, guint i)
478 {
479 		GtkTreeIter iter;
480 		char * name = g_strdup_printf (_("Group %d"), i);
481 		GObject *adjustment_to =
482 			G_OBJECT (gtk_adjustment_new (0, 0, G_MAXUSHORT, 1, 1, 1));
483 		GObject *adjustment_from =
484 			G_OBJECT (gtk_adjustment_new (0, 0, G_MAXUSHORT, 1, 1, 1));
485 		gtk_list_store_append (state->groups_list, &iter);
486 		gtk_list_store_set (state->groups_list, &iter,
487 				    GROUP_NAME, name,
488 				    GROUP_FROM, (guint) i,
489 				    GROUP_TO, (guint) i,
490 				    GROUP_ADJUSTMENT_FROM, adjustment_from,
491 				    GROUP_ADJUSTMENT_TO, adjustment_to,
492 				    -1);
493 		g_free (name);
494 }
495 
496 static void
dialog_kaplan_meier_tool_setup_treeview(KaplanMeierToolState * state)497 dialog_kaplan_meier_tool_setup_treeview (KaplanMeierToolState *state)
498 {
499 	guint i;
500 	GtkCellRenderer *renderer;
501 	GtkWidget *scrolled = go_gtk_builder_get_widget (state->base.gui, "groups-scrolled");
502 	GtkTreeSelection  *selection;
503 
504 	state->groups_treeview = GTK_TREE_VIEW (go_gtk_builder_get_widget
505 						(state->base.gui,
506 						 "groups-tree"));
507 	state->groups_list = gtk_list_store_new (GROUP_COLUMNS,
508 						 G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_OBJECT);
509 	state->groups_treeview = GTK_TREE_VIEW (gtk_tree_view_new_with_model
510 						(GTK_TREE_MODEL (state->groups_list)));
511 	g_object_unref (state->groups_list);
512 	selection = gtk_tree_view_get_selection (state->groups_treeview);
513 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
514 
515 	for (i = 0; i<2; i++)
516 		dialog_kaplan_meier_tool_treeview_add_item (state, i);
517 
518 	g_signal_connect (selection,
519 			  "changed",
520 			  G_CALLBACK (cb_selection_changed), state);
521 
522 	renderer = gtk_cell_renderer_text_new ();
523 	g_object_set (G_OBJECT (renderer),
524 		      "editable", TRUE,
525 		      NULL);
526 	gtk_tree_view_insert_column_with_attributes (state->groups_treeview,
527 						     -1, _("Group"),
528 						     renderer,
529 						     "text", GROUP_NAME,
530 						     NULL);
531 	g_signal_connect (G_OBJECT (renderer), "edited",
532 			  G_CALLBACK (cb_group_name_edited), state);
533 
534 	renderer = gtk_cell_renderer_spin_new ();
535 
536 	g_object_set (G_OBJECT (renderer), "editable", TRUE, "xalign", 1.0,
537 		      "digits", 0, NULL);
538 	g_signal_connect (G_OBJECT (renderer), "edited",
539 			  G_CALLBACK (cb_change_from), state);
540 	gtk_tree_view_insert_column_with_attributes (state->groups_treeview,
541 						     -1, _("From"),
542 						     renderer,
543 						     "text", GROUP_FROM,
544 						     "adjustment", GROUP_ADJUSTMENT_FROM,
545 						     NULL);
546 
547 	renderer = gtk_cell_renderer_spin_new ();
548 	g_object_set (G_OBJECT (renderer), "editable", TRUE, "xalign", 1.0,
549 		      "digits", 0, NULL);
550 	g_signal_connect (G_OBJECT (renderer), "edited",
551 			  G_CALLBACK (cb_change_to), state);
552 	gtk_tree_view_insert_column_with_attributes (state->groups_treeview,
553 						     -1, _("To"),
554 						     renderer,
555 						     "text", GROUP_TO,
556 						     "adjustment", GROUP_ADJUSTMENT_TO,
557 						     NULL);
558 
559 	gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (state->groups_treeview));
560 
561 	cb_selection_changed (selection, state);
562 }
563 
564 static gboolean
kaplan_meier_tool_add_group_cb(G_GNUC_UNUSED GtkWidget * dummy,KaplanMeierToolState * state)565 kaplan_meier_tool_add_group_cb (G_GNUC_UNUSED GtkWidget *dummy,
566 				KaplanMeierToolState *state)
567 {
568 	dialog_kaplan_meier_tool_treeview_add_item
569 		(state, gtk_tree_model_iter_n_children (GTK_TREE_MODEL (state->groups_list),
570                                                         NULL));
571 	return FALSE;
572 }
573 
574 static gboolean
kaplan_meier_tool_remove_group_cb(G_GNUC_UNUSED GtkWidget * dummy,KaplanMeierToolState * state)575 kaplan_meier_tool_remove_group_cb (G_GNUC_UNUSED GtkWidget *dummy,
576 				KaplanMeierToolState *state)
577 {
578 	GtkTreeSelection  *selection;
579 	GtkTreeIter iter;
580 
581 	selection = gtk_tree_view_get_selection (state->groups_treeview);
582 
583 	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
584 		gtk_list_store_remove ( state->groups_list, &iter);
585 	}
586 
587 	return FALSE;
588 }
589 
590 
591 /**
592  * dialog_kaplan_meier_tool:
593  * @wbcg:
594  * @sheet:
595  *
596  * Show the dialog (guru).
597  *
598  **/
599 int
dialog_kaplan_meier_tool(WBCGtk * wbcg,Sheet * sheet)600 dialog_kaplan_meier_tool (WBCGtk *wbcg, Sheet *sheet)
601 {
602         KaplanMeierToolState *state;
603 	GtkWidget *widget;
604 	char const * plugins[] = { "Gnumeric_fnstat",
605 				   "Gnumeric_fnlookup",
606 				   "Gnumeric_fnmath",
607 				   "Gnumeric_fninfo",
608 				   "Gnumeric_fnlogical",
609 				   NULL};
610 
611 	if ((wbcg == NULL) ||
612 	    gnm_check_for_plugins_missing (plugins, wbcg_toplevel (wbcg)))
613 		return 1;
614 
615 	/* Only pop up one copy per workbook */
616 	if (gnm_dialog_raise_if_exists (wbcg, KAPLAN_MEIER_KEY))
617 		return 0;
618 
619 	state = g_new0 (KaplanMeierToolState, 1);
620 
621 	if (dialog_tool_init (&state->base, wbcg, sheet,
622 			      GNUMERIC_HELP_LINK_KAPLAN_MEIER,
623 			      "res:ui/kaplan-meier.ui", "KaplanMeier",
624 			      _("Could not create the Kaplan Meier Tool dialog."),
625 			      KAPLAN_MEIER_KEY,
626 			      G_CALLBACK (kaplan_meier_tool_ok_clicked_cb), NULL,
627 			      G_CALLBACK (kaplan_meier_tool_update_sensitivity_cb),
628 			      0))
629 	{
630 		g_free(state);
631 		return 0;
632 	}
633 
634 
635 
636 	state->censorship_button = GTK_WIDGET (go_gtk_builder_get_widget
637 						  (state->base.gui,
638 						   "censor-button"));
639 	state->censor_spin_from = GTK_WIDGET (go_gtk_builder_get_widget
640 					      (state->base.gui,
641 					       "censored-spinbutton1"));
642 	gtk_spin_button_set_range (GTK_SPIN_BUTTON (state->censor_spin_from), 0.,G_MAXSHORT);
643 	state->censor_spin_to = GTK_WIDGET (go_gtk_builder_get_widget
644 					    (state->base.gui,
645 					     "censored-spinbutton2"));
646 	gtk_spin_button_set_range (GTK_SPIN_BUTTON (state->censor_spin_to), 0.,G_MAXSHORT);
647 	state->graph_button = GTK_WIDGET (go_gtk_builder_get_widget
648 						  (state->base.gui,
649 						   "graph-button"));
650 	state->tick_button = GTK_WIDGET (go_gtk_builder_get_widget
651 						  (state->base.gui,
652 						   "tick-button"));
653 	state->add_group_button = GTK_WIDGET (go_gtk_builder_get_widget
654 						  (state->base.gui,
655 						   "add-button"));
656 	state->remove_group_button = GTK_WIDGET (go_gtk_builder_get_widget
657 						  (state->base.gui,
658 						   "remove-button"));
659 	state->std_error_button = GTK_WIDGET (go_gtk_builder_get_widget
660 						  (state->base.gui,
661 						   "std-error-button"));
662 	state->logrank_button = GTK_WIDGET (go_gtk_builder_get_widget
663 						  (state->base.gui,
664 						   "logrank-button"));
665 
666 	state->groups_check = GTK_WIDGET (go_gtk_builder_get_widget
667 						  (state->base.gui,
668 						   "groups-check"));
669 	state->groups_grid = GTK_WIDGET (go_gtk_builder_get_widget
670 						  (state->base.gui,
671 						   "groups-grid"));
672 	state->groups_input = gnm_expr_entry_new (state->base.wbcg, TRUE);
673 	gnm_expr_entry_set_flags (state->groups_input, GNM_EE_FORCE_ABS_REF,
674 				  GNM_EE_MASK);
675 	gtk_grid_attach (GTK_GRID (state->groups_grid),
676 	                 GTK_WIDGET (state->groups_input), 1, 1, 2, 1);
677 
678 	dialog_kaplan_meier_tool_setup_treeview (state);
679 
680 	g_signal_connect_after (G_OBJECT (state->groups_check),
681 		"toggled",
682 		G_CALLBACK (kaplan_meier_tool_update_sensitivity_cb), state);
683 	g_signal_connect_after (G_OBJECT (state->censorship_button),
684 		"toggled",
685 		G_CALLBACK (kaplan_meier_tool_update_sensitivity_cb), state);
686 	g_signal_connect_after (G_OBJECT (state->graph_button),
687 		"toggled",
688 		G_CALLBACK (kaplan_meier_tool_update_sensitivity_cb), state);
689 	g_signal_connect_after (G_OBJECT (state->std_error_button),
690 		"toggled",
691 		G_CALLBACK (kaplan_meier_tool_update_sensitivity_cb), state);
692 	g_signal_connect_after (G_OBJECT (state->groups_input),
693 				"changed",
694 				G_CALLBACK (kaplan_meier_tool_update_sensitivity_cb),
695 				state);
696 
697 	g_signal_connect_after (G_OBJECT (state->groups_check),
698 		"toggled",
699 		G_CALLBACK (kaplan_meier_tool_update_groups_sensitivity_cb), state);
700 	g_signal_connect_after (G_OBJECT (state->tick_button),
701 		"toggled",
702 		G_CALLBACK (kaplan_meier_tool_set_graph_cb), state);
703 	g_signal_connect_after (G_OBJECT (state->add_group_button),
704 		"clicked",
705 		G_CALLBACK (kaplan_meier_tool_add_group_cb), state);
706 	g_signal_connect_after (G_OBJECT (state->remove_group_button),
707 		"clicked",
708 		G_CALLBACK (kaplan_meier_tool_remove_group_cb), state);
709 	g_signal_connect_after (G_OBJECT (state->censor_spin_from),
710 		"value-changed",
711 		G_CALLBACK (kaplan_meier_tool_set_censor_from_cb), state);
712 	g_signal_connect_after (G_OBJECT (state->censor_spin_to),
713 		"value-changed",
714 		G_CALLBACK (kaplan_meier_tool_set_censor_cb), state);
715 	g_signal_connect (G_OBJECT
716 			  (gnm_expr_entry_get_entry (
717 				  GNM_EXPR_ENTRY (state->base.input_entry_2))),
718 			  "focus-in-event",
719 			  G_CALLBACK (kaplan_meier_tool_set_censorship_cb), state);
720 	g_signal_connect (G_OBJECT
721 			  (gnm_expr_entry_get_entry (
722 				  GNM_EXPR_ENTRY (state->groups_input))),
723 			  "focus-in-event",
724 			  G_CALLBACK (kaplan_meier_tool_set_groups_cb), state);
725 
726 	gnm_editable_enters (GTK_WINDOW (state->base.dialog),
727 					  GTK_WIDGET (state->groups_input));
728 
729 	widget = go_gtk_builder_get_widget (state->base.gui, "groups-label");
730 	gtk_label_set_mnemonic_widget (GTK_LABEL (widget),
731 				       GTK_WIDGET (state->groups_input));
732 	go_atk_setup_label (widget, GTK_WIDGET (state->groups_input));
733 
734 	gnm_dao_set_put (GNM_DAO (state->base.gdao), TRUE, TRUE);
735 	kaplan_meier_tool_update_sensitivity_cb (NULL, state);
736 	kaplan_meier_tool_update_groups_sensitivity_cb (NULL, state);
737 	tool_load_selection ((GnmGenericToolState *)state, TRUE);
738 
739 	gtk_widget_show_all (GTK_WIDGET (state->base.dialog));
740 	/* And to hide the in-place button again */
741 	gnm_dao_set_inplace ( GNM_DAO (state->base.gdao), NULL);
742 
743         return 0;
744 }
745