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