cefa7f5a76124d6d463ffa9e7dc2759e6e1d3723
[pspp] / src / ui / gui / psppire-dialog-action-factor.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2009, 2010, 2011, 2012, 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-factor.h"
21
22 #include <float.h>
23
24 #include "psppire-var-view.h"
25 #include "dialog-common.h"
26 #include "psppire-selector.h"
27 #include "psppire-dict.h"
28 #include "psppire-dialog.h"
29 #include "builder-wrapper.h"
30 #include "psppire-scanf.h"
31 #include <libpspp/str.h>
32
33 #include "gettext.h"
34 #define _(msgid) gettext (msgid)
35 #define N_(msgid) msgid
36
37 static void psppire_dialog_action_factor_class_init      (PsppireDialogActionFactorClass *class);
38
39 G_DEFINE_TYPE (PsppireDialogActionFactor, psppire_dialog_action_factor, PSPPIRE_TYPE_DIALOG_ACTION);
40
41 static const char *rot_method_syntax[] = 
42   {
43     "NOROTATE",
44     "VARIMAX",
45     "QUARTIMAX",
46     "EQUAMAX"
47   };
48
49 static void
50 on_extract_toggle (GtkToggleButton *button, PsppireDialogActionFactor *f)
51 {
52   gboolean active = gtk_toggle_button_get_active (button);
53
54   gtk_widget_set_sensitive (GTK_WIDGET (f->n_factors), active);
55   gtk_widget_set_sensitive (GTK_WIDGET (f->mineigen), ! active);
56 }
57
58 static char *
59 generate_syntax (PsppireDialogAction *act)
60 {
61   PsppireDialogActionFactor *rd  = PSPPIRE_DIALOG_ACTION_FACTOR (act);
62
63   gchar *text = NULL;
64   struct string str;
65   ds_init_cstr (&str, "FACTOR ");
66
67   ds_put_cstr (&str, "\n\t/VARIABLES=");
68
69   psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (rd->variables), 0, &str);
70
71
72   ds_put_cstr (&str, "\n\t/CRITERIA = ");
73   if ( rd->extraction.explicit_nfactors )
74     ds_put_c_format (&str, "FACTORS (%d)", rd->extraction.n_factors);
75   else
76     ds_put_c_format (&str, "MINEIGEN (%.*g)",
77                      DBL_DIG + 1, rd->extraction.mineigen);
78
79   /*
80     The CRITERIA = ITERATE subcommand is overloaded.
81      It applies to the next /ROTATION and/or EXTRACTION command whatever comes first.
82   */
83   ds_put_c_format (&str, " ITERATE (%d)", rd->extraction.n_iterations);
84
85
86   ds_put_cstr (&str, "\n\t/EXTRACTION =");
87   if ( rd->extraction.paf)
88     ds_put_cstr (&str, "PAF");
89   else
90     ds_put_cstr (&str, "PC");
91
92
93   ds_put_cstr (&str, "\n\t/METHOD = ");
94   if ( rd->extraction.covariance )
95     ds_put_cstr (&str, "COVARIANCE");
96   else
97     ds_put_cstr (&str, "CORRELATION");
98
99   if ( rd->extraction.scree )
100     {
101       ds_put_cstr (&str, "\n\t/PLOT = ");
102       ds_put_cstr (&str, "EIGEN");
103     }
104
105   ds_put_cstr (&str, "\n\t/PRINT = ");
106   ds_put_cstr (&str, "INITIAL ");
107
108   if ( rd->extraction.unrotated )  
109     ds_put_cstr (&str, "EXTRACTION ");
110
111   if ( rd->rotation.rotated_solution )
112     ds_put_cstr (&str, "ROTATION");
113
114
115   /* The CRITERIA = ITERATE subcommand is overloaded.
116      It applies to the next /ROTATION and/or EXTRACTION command whatever comes first.
117   */
118   ds_put_c_format (&str, "\n\t/CRITERIA = ITERATE (%d)",  rd->rotation.iterations  );
119
120   ds_put_cstr (&str, "\n\t/ROTATION = ");
121   ds_put_cstr (&str, rot_method_syntax[rd->rotation.method]);
122
123   ds_put_cstr (&str, ".");
124   text = ds_steal_cstr (&str);
125
126   ds_destroy (&str);
127
128   return text;
129 }
130
131 static void
132 load_rotation_parameters (PsppireDialogActionFactor *fd, const struct rotation_parameters *p)
133 {
134   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->display_rotated_solution),
135                                 p->rotated_solution);
136   
137   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->rotate_iterations),
138                              p->iterations);
139
140   switch (p->method)
141     {
142     case ROT_NONE:
143       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->rotation_none), TRUE);
144       break;
145     case ROT_VARIMAX:
146       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->rotation_varimax), TRUE);
147       break;
148     case ROT_QUARTIMAX:
149       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->rotation_quartimax), TRUE);
150       break;
151     case ROT_EQUIMAX:
152       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->rotation_equimax), TRUE);
153       break;
154     default:
155       g_assert_not_reached ();
156       break;
157     }
158 }
159
160 static void
161 load_extraction_parameters (PsppireDialogActionFactor *fd, const struct extraction_parameters *p)
162 {
163   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->mineigen), p->mineigen);
164   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->n_factors), p->n_factors);
165
166   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->extract_iterations), p->n_iterations);
167
168
169   if (p->explicit_nfactors)
170     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->nfactors_toggle), TRUE);
171   else
172     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->mineigen_toggle), TRUE);
173
174   if (p->covariance)
175     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->covariance_toggle), TRUE);
176   else
177     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->correlation_toggle), TRUE);
178
179
180   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->scree_button), p->scree);
181   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->unrotated_button), p->unrotated);
182
183   if ( p->paf )
184     gtk_combo_box_set_active (GTK_COMBO_BOX (fd->extraction_combo), 1);
185   else
186     gtk_combo_box_set_active (GTK_COMBO_BOX (fd->extraction_combo), 0);
187     
188 }
189
190 static gboolean
191 dialog_state_valid (PsppireDialogActionFactor *da)
192 {
193   GtkTreeModel *liststore = gtk_tree_view_get_model (GTK_TREE_VIEW (da->variables));
194
195   if  (gtk_tree_model_iter_n_children (liststore, NULL) < 2)
196     return FALSE;
197
198   return TRUE;
199 }
200
201 static const struct extraction_parameters default_extraction_parameters =
202   {1.0, 0, 25, FALSE, TRUE, FALSE, TRUE, FALSE};
203
204 static const struct rotation_parameters default_rotation_parameters =
205   {TRUE, 25, ROT_VARIMAX};
206
207 static void
208 dialog_refresh (PsppireDialogAction *da)
209 {
210   PsppireDialogActionFactor *fd  = PSPPIRE_DIALOG_ACTION_FACTOR (da);
211   GtkTreeModel *liststore =
212     gtk_tree_view_get_model (GTK_TREE_VIEW (fd->variables));
213   gtk_list_store_clear (GTK_LIST_STORE (liststore));
214
215   load_extraction_parameters (fd, &default_extraction_parameters);
216   load_rotation_parameters (fd, &default_rotation_parameters);
217 }
218
219
220
221 static void
222 set_rotation_parameters (PsppireDialogActionFactor *act, struct rotation_parameters *p)
223 {
224   p->iterations = gtk_spin_button_get_value (GTK_SPIN_BUTTON (act->rotate_iterations));
225   p->rotated_solution = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->display_rotated_solution));
226
227
228   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->rotation_none)))
229     p->method = ROT_NONE;
230
231   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->rotation_varimax)))
232     p->method = ROT_VARIMAX;
233
234   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->rotation_quartimax)))
235     p->method = ROT_QUARTIMAX;
236
237   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->rotation_equimax)))
238     p->method = ROT_EQUIMAX;
239 }
240
241 static void
242 set_extraction_parameters (PsppireDialogActionFactor *act, struct extraction_parameters *p)
243 {
244   p->mineigen = gtk_spin_button_get_value (GTK_SPIN_BUTTON (act->mineigen));
245   p->n_factors = gtk_spin_button_get_value (GTK_SPIN_BUTTON (act->n_factors));
246   p->n_iterations = gtk_spin_button_get_value (GTK_SPIN_BUTTON (act->extract_iterations));
247
248   p->explicit_nfactors = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->nfactors_toggle));
249   p->covariance = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->covariance_toggle));
250
251   p->scree = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->scree_button));
252   p->unrotated = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->unrotated_button));
253
254   p->paf = (gtk_combo_box_get_active (GTK_COMBO_BOX (act->extraction_combo)) == 1);
255 }
256
257
258 static void
259 run_extractions_subdialog (PsppireDialogActionFactor *act)
260 {
261   struct extraction_parameters *ex = &act->extraction;
262
263   gint response = psppire_dialog_run (PSPPIRE_DIALOG (act->extraction_dialog));
264
265   if ( response == PSPPIRE_RESPONSE_CONTINUE )
266     {
267       /* Set the parameters from their respective widgets */
268       set_extraction_parameters (act, ex);
269     }
270   else
271     {
272       /* Cancelled.  Reset the widgets to their old state */
273       load_extraction_parameters (act, ex);
274     }
275 }
276
277 static void
278 run_rotations_subdialog (PsppireDialogActionFactor *act)
279 {
280   struct rotation_parameters *rot = &act->rotation;
281
282   gint response = psppire_dialog_run (PSPPIRE_DIALOG (act->rotation_dialog));
283
284   if ( response == PSPPIRE_RESPONSE_CONTINUE )
285     {
286       /* Set the parameters from their respective widgets */
287       set_rotation_parameters (act, rot);
288     }
289   else
290     {
291       /* Cancelled.  Reset the widgets to their old state */
292       load_rotation_parameters (act, rot);
293     }
294 }
295
296 static void
297 psppire_dialog_action_factor_activate (GtkAction *a)
298 {
299   PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
300   PsppireDialogActionFactor *act = PSPPIRE_DIALOG_ACTION_FACTOR (a);
301   GtkWidget *extraction_button ;
302   GtkWidget *rotation_button ;
303
304   GHashTable *thing = psppire_dialog_action_get_hash_table (pda);
305   GtkBuilder *xml = g_hash_table_lookup (thing, a);
306   if (!xml)
307     {
308       xml = builder_new ("factor.ui");
309       g_hash_table_insert (thing, a, xml);
310
311
312       pda->dialog = get_widget_assert   (xml, "factor-dialog");
313       pda->source = get_widget_assert   (xml, "dict-view");
314
315       extraction_button = get_widget_assert (xml, "button-extractions");
316       rotation_button = get_widget_assert (xml, "button-rotations");
317
318       act->extraction_dialog = get_widget_assert (xml, "extractions-dialog");
319       act->rotation_dialog = get_widget_assert (xml, "rotations-dialog");
320
321       act->variables = get_widget_assert   (xml, "psppire-var-view1");
322
323       {
324         GtkWidget *hbox = get_widget_assert (xml, "hbox6");
325         GtkWidget *eigenvalue_extraction ;
326
327         act->mineigen_toggle = get_widget_assert (xml, "mineigen-radiobutton");
328
329         eigenvalue_extraction = psppire_scanf_new (_("_Eigenvalues over %4.2f times the mean eigenvalue"), &act->mineigen);
330
331         g_object_set (eigenvalue_extraction,
332                       "use-underline", TRUE,
333                       "mnemonic-widget", act->mineigen_toggle,
334                       NULL);
335
336         act->nfactors_toggle = get_widget_assert (xml, "nfactors-radiobutton");
337         act->n_factors = get_widget_assert (xml, "spinbutton-nfactors");
338         act->extract_iterations = get_widget_assert (xml, "spinbutton-extract-iterations");
339         act->covariance_toggle = get_widget_assert (xml,  "covariance-radiobutton");
340         act->correlation_toggle = get_widget_assert (xml, "correlations-radiobutton");
341
342         act->scree_button = get_widget_assert (xml, "scree-button");
343         act->unrotated_button = get_widget_assert (xml, "unrotated-button");
344         act->extraction_combo = get_widget_assert (xml, "combobox1");
345
346         gtk_container_add (GTK_CONTAINER (hbox), eigenvalue_extraction);
347
348         g_signal_connect (act->nfactors_toggle, "toggled", G_CALLBACK (on_extract_toggle), act);
349
350         gtk_widget_show_all (eigenvalue_extraction);
351       }
352
353       {
354         act->rotate_iterations = get_widget_assert (xml, "spinbutton-rot-iterations");
355
356         act->display_rotated_solution = get_widget_assert (xml, "checkbutton-rotated-solution");
357
358         act->rotation_none      = get_widget_assert (xml, "radiobutton-none");
359         act->rotation_varimax   = get_widget_assert (xml, "radiobutton-varimax");
360         act->rotation_quartimax = get_widget_assert (xml, "radiobutton-quartimax");
361         act->rotation_equimax   = get_widget_assert (xml, "radiobutton-equimax");
362       }
363
364       g_signal_connect_swapped (extraction_button, "clicked",
365                                 G_CALLBACK (run_extractions_subdialog), act);
366       g_signal_connect_swapped (rotation_button, "clicked", G_CALLBACK (run_rotations_subdialog), act);
367
368     }
369
370   psppire_dialog_action_set_valid_predicate (pda, (void *) dialog_state_valid);
371   psppire_dialog_action_set_refresh (pda, dialog_refresh);
372
373   PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_factor_parent_class)->activate (pda);
374 }
375
376 static void
377 psppire_dialog_action_factor_class_init (PsppireDialogActionFactorClass *class)
378 {
379   psppire_dialog_action_set_activation (class, psppire_dialog_action_factor_activate);
380
381   PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax;
382 }
383
384 static void
385 psppire_dialog_action_factor_init (PsppireDialogActionFactor *act)
386 {
387 }