Oneway anova dialog: Converted to PsppireDialogAction
[pspp] / src / ui / gui / psppire-dialog-action-oneway.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2012, 2013  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17
18 #include <config.h>
19
20 #include "psppire-dialog-action-oneway.h"
21
22 #include "psppire-var-view.h"
23 #include "psppire-acr.h"
24
25 #include "psppire-dialog.h"
26 #include "builder-wrapper.h"
27 #include "helper.h"
28
29
30 #include "gettext.h"
31 #define _(msgid) gettext (msgid)
32 #define N_(msgid) msgid
33
34
35 static void next (GtkWidget *widget, PsppireDialogActionOneway *);
36 static void prev (GtkWidget *widget, PsppireDialogActionOneway *);
37 static void run_contrasts_dialog (PsppireDialogActionOneway *csd);
38 static void push_new_store (GArray *contrast_stack, PsppireDialogActionOneway *csd);
39
40
41 static void psppire_dialog_action_oneway_init            (PsppireDialogActionOneway      *act);
42 static void psppire_dialog_action_oneway_class_init      (PsppireDialogActionOnewayClass *class);
43
44 G_DEFINE_TYPE (PsppireDialogActionOneway, psppire_dialog_action_oneway, PSPPIRE_TYPE_DIALOG_ACTION);
45
46
47 static char *
48 generate_syntax (PsppireDialogAction *act)
49 {
50   PsppireDialogActionOneway *ow = PSPPIRE_DIALOG_ACTION_ONEWAY (act);
51   gchar *text;
52   gint i;
53
54   gboolean descriptives = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ow->descriptives));
55   gboolean homogeneity = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ow->homogeneity));
56   struct string dss;
57
58   ds_init_cstr (&dss, "ONEWAY /VARIABLES=");
59
60   psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (ow->vars_treeview), 0, &dss);
61
62   ds_put_cstr (&dss, " BY ");
63
64   ds_put_cstr (&dss, gtk_entry_get_text (GTK_ENTRY (ow->factor_entry)));
65
66   if (descriptives || homogeneity )
67     {
68       ds_put_cstr (&dss, "\n\t/STATISTICS=");
69       if (descriptives)
70         ds_put_cstr (&dss, "DESCRIPTIVES ");
71       if (homogeneity)
72         ds_put_cstr (&dss, "HOMOGENEITY ");
73     }
74
75   for (i = 0 ; i < ow->contrasts_array->len ; ++i )
76     {
77       GtkListStore *ls = g_array_index (ow->contrasts_array, GtkListStore*, i);
78       GtkTreeIter iter;
79       gboolean ok;
80
81       ds_put_cstr (&dss, "\n\t/CONTRAST=");
82
83       for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(ls),
84                                                &iter);
85            ok;
86            ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ls), &iter))
87         {
88           gdouble v;
89
90           gtk_tree_model_get (GTK_TREE_MODEL (ls), &iter, 0, &v, -1);
91
92           ds_put_c_format (&dss, " %g", v);
93         }
94     }
95
96   ds_put_cstr (&dss, ".\n");
97
98   text = ds_steal_cstr (&dss);
99   ds_destroy (&dss);
100
101   return text;
102 }
103
104
105 static gboolean
106 dialog_state_valid (gpointer data)
107 {
108   PsppireDialogActionOneway *ow = PSPPIRE_DIALOG_ACTION_ONEWAY (data);
109
110   GtkTreeModel *vars =
111     gtk_tree_view_get_model (GTK_TREE_VIEW (ow->vars_treeview));
112
113   GtkTreeIter notused;
114
115   if ( !gtk_tree_model_get_iter_first (vars, &notused) )
116     return FALSE;
117
118   if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (ow->factor_entry))))
119     return FALSE;
120
121
122   return TRUE;
123 }
124
125 static void
126 refresh (PsppireDialogAction *rd_)
127 {
128   PsppireDialogActionOneway *ow = PSPPIRE_DIALOG_ACTION_ONEWAY (rd_);
129
130   GtkTreeModel *model =
131     gtk_tree_view_get_model (GTK_TREE_VIEW (ow->vars_treeview));
132
133   gtk_entry_set_text (GTK_ENTRY (ow->factor_entry), "");
134
135   gtk_list_store_clear (GTK_LIST_STORE (model));
136 }
137
138
139 /* Callback for when the list store currently associated with the
140    treeview has changed.  It sets the widgets of the subdialog
141    to reflect the store's new state.
142 */
143 static void
144 list_store_changed (PsppireDialogActionOneway *csd)
145 {
146   gboolean ok;
147   gdouble total = 0.0;
148   GtkTreeIter iter;
149   GtkTreeModel *ls = NULL;
150   gchar *text =
151     g_strdup_printf (_("Contrast %d of %d"),
152                      csd->c, csd->temp_contrasts->len);
153
154   gtk_label_set_label (GTK_LABEL (csd->stack_label), text);
155
156   g_free (text);
157
158   gtk_widget_set_sensitive (csd->prev, csd->c > 1);
159
160   if ( csd->c > 0 )
161     ls = g_array_index (csd->temp_contrasts, GtkTreeModel*, csd->c - 1);
162
163   psppire_acr_set_model (PSPPIRE_ACR (csd->acr), GTK_LIST_STORE (ls));
164
165   /* Sensitive iff the liststore has two items or more */
166   gtk_widget_set_sensitive (csd->next,
167                             gtk_tree_model_iter_nth_child
168                             (ls, &iter,  NULL, 1));
169
170   for (ok = gtk_tree_model_get_iter_first (ls, &iter);
171        ok;
172        ok = gtk_tree_model_iter_next (ls, &iter)
173        )
174     {
175       gdouble v;
176       gtk_tree_model_get (ls, &iter, 0, &v, -1);
177       total += v;
178     }
179
180   text = g_strdup_printf ("%g", total);
181
182   gtk_entry_set_text (GTK_ENTRY (csd->ctotal), text);
183
184   g_free (text);
185 }
186
187
188 /* Copy the contrasts array into the local array */
189 static GArray *
190 clone_contrasts_array (GArray *src_array)
191 {
192   gint i;
193
194   GArray *dest_array =
195     g_array_sized_new (FALSE, FALSE, sizeof (GtkListStore *),
196                        src_array->len);
197
198   for (i = 0 ; i < src_array->len ; ++i )
199     {
200
201       GtkTreeIter src_iter;
202       GtkListStore *src = g_array_index (src_array, GtkListStore*, i);
203       GtkListStore *dest;
204
205       /* Refuse to copy empty stores */
206       if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (src),
207                                            &src_iter))
208         continue;
209
210       dest = clone_list_store (src);
211
212       g_array_append_val (dest_array, dest);
213     }
214
215   return dest_array;
216 }
217
218
219
220
221 static void
222 psppire_dialog_action_oneway_activate (GtkAction *a)
223 {
224   PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
225   PsppireDialogActionOneway *act = PSPPIRE_DIALOG_ACTION_ONEWAY (a);
226
227   GtkBuilder *xml = builder_new ("oneway.ui");
228   GtkWidget *contrasts_button =
229     get_widget_assert (xml, "contrasts-button");
230   GtkEntry *entry = GTK_ENTRY (get_widget_assert (xml, "entry1"));
231
232   pda->dialog = get_widget_assert   (xml, "oneway-anova-dialog");
233   pda->source = get_widget_assert   (xml, "oneway-anova-treeview1");
234
235   act->vars_treeview =  get_widget_assert (xml, "oneway-anova-treeview2");
236   act->factor_entry = get_widget_assert (xml, "oneway-anova-entry");
237
238   act->descriptives =  get_widget_assert (xml, "checkbutton1");
239   act->homogeneity =  get_widget_assert (xml, "checkbutton2");
240
241   act->contrasts_dialog = get_widget_assert (xml, "contrasts-dialog");
242   
243   act->next = get_widget_assert (xml, "next-button");
244   act->prev = get_widget_assert (xml, "prev-button");
245   act->ctotal = get_widget_assert (xml, "entry2");
246   act->acr = get_widget_assert (xml, "psppire-acr1");
247   act->stack_label = get_widget_assert (xml, "contrast-stack-label");
248   act->contrasts_array = g_array_new (FALSE, FALSE, sizeof (GtkListStore *));
249
250
251   g_signal_connect (act->next, "clicked", G_CALLBACK (next), act);
252   g_signal_connect (act->prev, "clicked", G_CALLBACK (prev), act);
253
254   psppire_acr_set_entry (PSPPIRE_ACR (act->acr), entry);
255
256   gtk_window_set_transient_for (GTK_WINDOW (act->contrasts_dialog),
257                                   GTK_WINDOW (pda->toplevel));
258
259
260   g_signal_connect_swapped (contrasts_button, "clicked",
261                     G_CALLBACK (run_contrasts_dialog), act);
262
263
264   psppire_dialog_action_set_valid_predicate (pda, dialog_state_valid);
265   psppire_dialog_action_set_refresh (pda, refresh);
266
267   g_object_unref (xml);
268
269   if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_oneway_parent_class)->activate)
270     PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_oneway_parent_class)->activate (pda);
271 }
272
273 static void
274 psppire_dialog_action_oneway_class_init (PsppireDialogActionOnewayClass *class)
275 {
276   GtkActionClass *action_class = GTK_ACTION_CLASS (class);
277
278   action_class->activate = psppire_dialog_action_oneway_activate;
279   PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax;
280 }
281
282
283 static void
284 psppire_dialog_action_oneway_init (PsppireDialogActionOneway *act)
285 {
286   act->contrasts_array = NULL;
287   act->c = -1;
288
289 }
290
291
292 \f
293
294 static void
295 run_contrasts_dialog (PsppireDialogActionOneway *csd)
296 {
297   gint response;
298
299   csd->temp_contrasts = clone_contrasts_array (csd->contrasts_array);
300
301   csd->c = 1;
302
303   push_new_store (csd->temp_contrasts, csd);
304
305   response = psppire_dialog_run (PSPPIRE_DIALOG (csd->contrasts_dialog));
306
307   if ( response == PSPPIRE_RESPONSE_CONTINUE )
308     {
309       csd->contrasts_array = clone_contrasts_array (csd->temp_contrasts);
310     }
311 }
312
313
314 static void
315 push_new_store (GArray *contrast_stack, PsppireDialogActionOneway *csd)
316 {
317   GtkListStore *ls = gtk_list_store_new (1, G_TYPE_DOUBLE);
318
319   g_array_append_val (contrast_stack, ls);
320
321   g_signal_connect_swapped (ls, "row-deleted",
322                             G_CALLBACK (list_store_changed), csd);
323
324   g_signal_connect_swapped (ls, "row-changed",
325                             G_CALLBACK (list_store_changed), csd);
326
327   list_store_changed (csd);
328 }
329
330
331 static void
332 next (GtkWidget *widget, PsppireDialogActionOneway *csd)
333 {
334   if (csd->c >= csd->temp_contrasts->len)
335     push_new_store (csd->temp_contrasts, csd);
336
337   csd->c++;
338
339   list_store_changed (csd);
340 }
341
342
343 static void
344 prev (GtkWidget *widget, PsppireDialogActionOneway *csd)
345 {
346   if ( csd->c > 0 )
347     --csd->c;
348
349   list_store_changed (csd);
350 }