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