Implemented a rotations subdialog for the FACTOR command
[pspp-builds.git] / src / ui / gui / factor-dialog.c
index 1480e0675b43d38acc4ef2ce99663ef4bae89413..61427cc23285f25a928afdae15a664af0a8f120e 100644 (file)
@@ -45,7 +45,7 @@ struct extraction_parameters
 {
   gdouble mineigen;
   gint n_factors;
-  gint iterations;
+  gint n_iterations;
 
   gboolean explicit_nfactors;  
   gboolean covariance;
@@ -56,9 +56,35 @@ struct extraction_parameters
   gboolean paf;
 };
 
+enum rotation_type {
+  ROT_NONE,
+  ROT_VARIMAX,
+  ROT_QUARTIMAX,
+  ROT_EQUIMAX
+};
+
+static const char *rot_method_syntax[] = 
+  {
+    "NOROTATE",
+    "VARIMAX",
+    "QUARTIMAX",
+    "EQUAMAX"
+  };
+
+struct rotation_parameters
+{
+  gboolean rotated_solution;
+  gint iterations;
+
+  enum rotation_type method;
+};
+
+
 
 static const struct extraction_parameters default_extraction_parameters = {1.0, 0, 25, FALSE, TRUE, FALSE, TRUE, FALSE};
 
+static const struct rotation_parameters default_rotation_parameters = {TRUE, 25, ROT_VARIMAX};
+
 struct factor
 {
   GtkBuilder *xml;
@@ -69,10 +95,11 @@ struct factor
 
   /* The Extraction subdialog */
   GtkWidget *extraction_dialog;
+  GtkWidget *rotation_dialog;
 
   GtkWidget *n_factors;
   GtkWidget *mineigen;
-  GtkWidget *iterations;
+  GtkWidget *extract_iterations;
 
   GtkWidget *nfactors_toggle;
   GtkWidget *mineigen_toggle;
@@ -85,15 +112,57 @@ struct factor
 
   GtkWidget *extraction_combo;
 
+
+  /* Rotation Widgets */
+  GtkWidget *rotate_iterations;
+  GtkWidget *display_rotated_solution;
+  GtkWidget *rotation_none;
+  GtkWidget *rotation_varimax;
+  GtkWidget *rotation_quartimax;
+  GtkWidget *rotation_equimax;
+
+
   struct extraction_parameters extraction;
+  struct rotation_parameters rotation;
 };
 
+static void
+load_rotation_parameters (struct factor *fd, const struct rotation_parameters *p)
+{
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->display_rotated_solution),
+                               p->rotated_solution);
+  
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->rotate_iterations),
+                            p->iterations);
+
+  switch (p->method)
+    {
+    case ROT_NONE:
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->rotation_none), TRUE);
+      break;
+    case ROT_VARIMAX:
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->rotation_varimax), TRUE);
+      break;
+    case ROT_QUARTIMAX:
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->rotation_quartimax), TRUE);
+      break;
+    case ROT_EQUIMAX:
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->rotation_equimax), TRUE);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
 static void
 load_extraction_parameters (struct factor *fd, const struct extraction_parameters *p)
 {
   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->mineigen), p->mineigen);
   gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->n_factors), p->n_factors);
-  gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->iterations), p->iterations);
+
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (fd->extract_iterations), p->n_iterations);
+
 
   if (p->explicit_nfactors)
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->nfactors_toggle), TRUE);
@@ -117,11 +186,31 @@ load_extraction_parameters (struct factor *fd, const struct extraction_parameter
 }
 
 static void
-set_extraction_parameters (struct extraction_parameters *p, const struct factor *fd)
+set_rotation_parameters (const struct factor *fd, struct rotation_parameters *p)
+{
+  p->iterations = gtk_spin_button_get_value (GTK_SPIN_BUTTON (fd->rotate_iterations));
+  p->rotated_solution = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->display_rotated_solution));
+
+
+  if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->rotation_none)))
+    p->method = ROT_NONE;
+
+  if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->rotation_varimax)))
+    p->method = ROT_VARIMAX;
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->rotation_quartimax)))
+    p->method = ROT_QUARTIMAX;
+
+  if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->rotation_equimax)))
+    p->method = ROT_EQUIMAX;
+}
+
+static void
+set_extraction_parameters (const struct factor *fd, struct extraction_parameters *p)
 {
   p->mineigen = gtk_spin_button_get_value (GTK_SPIN_BUTTON (fd->mineigen));
   p->n_factors = gtk_spin_button_get_value (GTK_SPIN_BUTTON (fd->n_factors));
-  p->iterations = gtk_spin_button_get_value (GTK_SPIN_BUTTON (fd->iterations));
+  p->n_iterations = gtk_spin_button_get_value (GTK_SPIN_BUTTON (fd->extract_iterations));
 
   p->explicit_nfactors = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->nfactors_toggle));
   p->covariance = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->covariance_toggle));
@@ -132,7 +221,8 @@ set_extraction_parameters (struct extraction_parameters *p, const struct factor
   p->paf = (gtk_combo_box_get_active (GTK_COMBO_BOX (fd->extraction_combo)) == 1);
 }
 
-static void run_extractions_subdialog (struct factor *fd)
+static void
+run_extractions_subdialog (struct factor *fd)
 {
   struct extraction_parameters *ex = &fd->extraction;
 
@@ -141,7 +231,7 @@ static void run_extractions_subdialog (struct factor *fd)
   if ( response == PSPPIRE_RESPONSE_CONTINUE )
     {
       /* Set the parameters from their respective widgets */
-      set_extraction_parameters (ex, fd);
+      set_extraction_parameters (fd, ex);
     }
   else
     {
@@ -150,6 +240,25 @@ static void run_extractions_subdialog (struct factor *fd)
     }
 }
 
+static void
+run_rotations_subdialog (struct factor *fd)
+{
+  struct rotation_parameters *rot = &fd->rotation;
+
+  gint response = psppire_dialog_run (PSPPIRE_DIALOG (fd->rotation_dialog));
+
+  if ( response == PSPPIRE_RESPONSE_CONTINUE )
+    {
+      /* Set the parameters from their respective widgets */
+      set_rotation_parameters (fd, rot);
+    }
+  else
+    {
+      /* Cancelled.  Reset the widgets to their old state */
+      load_rotation_parameters (fd, rot);
+    }
+}
+
 
 static char * generate_syntax (const struct factor *rd);
 
@@ -162,6 +271,7 @@ refresh (struct factor *fd)
   gtk_list_store_clear (GTK_LIST_STORE (liststore));
 
   load_extraction_parameters (fd, &default_extraction_parameters);
+  load_rotation_parameters (fd, &default_rotation_parameters);
 }
 
 
@@ -210,16 +320,20 @@ factor_dialog (PsppireDataWindow *dw)
   GtkWidget *dialog ;
   GtkWidget *source ;
   GtkWidget *extraction_button ;
+  GtkWidget *rotation_button ;
 
   fd.xml = builder_new ("factor.ui");
 
   fd.extraction = default_extraction_parameters;
+  fd.rotation = default_rotation_parameters;
   
   dialog = get_widget_assert   (fd.xml, "factor-dialog");
   source = get_widget_assert   (fd.xml, "dict-view");
-  extraction_button = get_widget_assert (fd.xml, "button-extraction");
+  extraction_button = get_widget_assert (fd.xml, "button-extractions");
+  rotation_button = get_widget_assert (fd.xml, "button-rotations");
 
   fd.extraction_dialog = get_widget_assert (fd.xml, "extractions-dialog");
+  fd.rotation_dialog = get_widget_assert (fd.xml, "rotations-dialog");
 
   fd.de = dw;
 
@@ -232,7 +346,7 @@ factor_dialog (PsppireDataWindow *dw)
     fd.nfactors_toggle = get_widget_assert (fd.xml, "nfactors-radiobutton");
     fd.mineigen_toggle = get_widget_assert (fd.xml, "mineigen-radiobutton");
     fd.n_factors = get_widget_assert (fd.xml, "spinbutton-nfactors");
-    fd.iterations = get_widget_assert (fd.xml, "spinbutton-iterations");
+    fd.extract_iterations = get_widget_assert (fd.xml, "spinbutton-extract-iterations");
     fd.covariance_toggle = get_widget_assert (fd.xml,  "covariance-radiobutton");
     fd.correlation_toggle = get_widget_assert (fd.xml, "correlations-radiobutton");
 
@@ -247,7 +361,19 @@ factor_dialog (PsppireDataWindow *dw)
     gtk_widget_show_all (eigenvalue_extraction);
   }
 
+  {
+    fd.rotate_iterations = get_widget_assert (fd.xml, "spinbutton-rot-iterations");
+
+    fd.display_rotated_solution = get_widget_assert (fd.xml, "checkbutton-rotated-solution");
+
+    fd.rotation_none      = get_widget_assert (fd.xml, "radiobutton-none");
+    fd.rotation_varimax   = get_widget_assert (fd.xml, "radiobutton-varimax");
+    fd.rotation_quartimax = get_widget_assert (fd.xml, "radiobutton-quartimax");
+    fd.rotation_equimax   = get_widget_assert (fd.xml, "radiobutton-equimax");
+  }
+
   g_signal_connect_swapped (extraction_button, "clicked", G_CALLBACK (run_extractions_subdialog), &fd);
+  g_signal_connect_swapped (rotation_button, "clicked", G_CALLBACK (run_rotations_subdialog), &fd);
 
   g_signal_connect_swapped (fd.extraction_dialog, "show", G_CALLBACK (on_show), &fd);
 
@@ -257,6 +383,7 @@ factor_dialog (PsppireDataWindow *dw)
 
   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fd.de));
   gtk_window_set_transient_for (GTK_WINDOW (fd.extraction_dialog), GTK_WINDOW (fd.de));
+  gtk_window_set_transient_for (GTK_WINDOW (fd.rotation_dialog), GTK_WINDOW (fd.de));
 
   g_object_get (vs, "dictionary", &fd.dict, NULL);
   g_object_set (source, "model", fd.dict, NULL);
@@ -311,6 +438,20 @@ generate_syntax (const struct factor *rd)
 
   psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->variables), 0, string);
 
+
+  g_string_append (string, "\n\t/CRITERIA = ");
+  if ( rd->extraction.explicit_nfactors )
+    g_string_append_printf (string, "FACTORS (%d)", rd->extraction.n_factors);
+  else
+    g_string_append_printf (string, "MINEIGEN (%g)", rd->extraction.mineigen);
+
+  /*
+    The CRITERIA = ITERATE subcommand is overloaded.
+     It applies to the next /ROTATION and/or EXTRACTION command whatever comes first.
+  */
+  g_string_append_printf (string, " ITERATE (%d)", rd->extraction.n_iterations);
+
+
   g_string_append (string, "\n\t/EXTRACTION =");
   if ( rd->extraction.paf)
     g_string_append (string, "PAF");
@@ -318,6 +459,8 @@ generate_syntax (const struct factor *rd)
     g_string_append (string, "PC");
 
 
+
+
   g_string_append (string, "\n\t/METHOD = ");
   if ( rd->extraction.covariance )
     g_string_append (string, "COVARIANCE");
@@ -325,12 +468,6 @@ generate_syntax (const struct factor *rd)
     g_string_append (string, "CORRELATION");
 
 
-  g_string_append (string, "\n\t/CRITERIA = ");
-  if ( rd->extraction.explicit_nfactors )
-    g_string_append_printf (string, "FACTORS (%d)", rd->extraction.n_factors);
-  else
-    g_string_append_printf (string, "MINEIGEN (%g)", rd->extraction.mineigen);
-    
 
   if ( rd->extraction.scree )
     {
@@ -340,9 +477,21 @@ generate_syntax (const struct factor *rd)
 
   g_string_append (string, "\n\t/PRINT = ");
   g_string_append (string, "INITIAL ");
+
   if ( rd->extraction.unrotated )  
     g_string_append (string, "EXTRACTION ");
-  g_string_append (string, "ROTATION");
+
+  if ( rd->rotation.rotated_solution )
+    g_string_append (string, "ROTATION");
+
+
+  /* The CRITERIA = ITERATE subcommand is overloaded.
+     It applies to the next /ROTATION and/or EXTRACTION command whatever comes first.
+  */
+  g_string_append_printf (string, "\n\t/CRITERIA = ITERATE (%d)",  rd->rotation.iterations  );
+
+  g_string_append (string, "\n\t/ROTATION = ");
+  g_string_append (string, rot_method_syntax[rd->rotation.method]);
 
 
   g_string_append (string, ".\n");