Implemented a rotations subdialog for the FACTOR command
authorJohn Darrington <john@darrington.wattle.id.au>
Fri, 21 May 2010 07:59:30 +0000 (09:59 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Fri, 21 May 2010 07:59:30 +0000 (09:59 +0200)
src/ui/gui/factor-dialog.c
src/ui/gui/factor.ui

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");
index 2bf61b2bd94d585751700e21dd2ecec32a849a36..4d2c612d7b69cbcc062ccf4a633162d4d92b93a3 100644 (file)
             <property name="layout_style">spread</property>
             <child>
               <object class="GtkButton" id="button-descriptives">
-                <property name="label" translatable="yes">Descriptives...</property>
-                <property name="visible">False</property>
+                <property name="label" translatable="yes">_Descriptives...</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
               </object>
               <packing>
                 <property name="expand">False</property>
               </packing>
             </child>
             <child>
-              <object class="GtkButton" id="button-extraction">
-                <property name="label" translatable="yes">Extraction...</property>
+              <object class="GtkButton" id="button-extractions">
+                <property name="label" translatable="yes">_Extraction...</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
               </object>
               <packing>
                 <property name="expand">False</property>
               </packing>
             </child>
             <child>
-              <placeholder/>
+              <object class="GtkButton" id="button-rotations">
+                <property name="label" translatable="yes">_Rotations...</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
             </child>
           </object>
           <packing>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkSpinButton" id="spinbutton-iterations">
+                      <object class="GtkSpinButton" id="spinbutton-extract-iterations">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="invisible_char">&#x2022;</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkAdjustment" id="adjustment3">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="PsppireDialog" id="rotations-dialog">
+    <property name="title" translatable="yes">Factor Analysis: Rotation</property>
+    <property name="modal">True</property>
+    <child internal-child="hbox">
+      <object class="GtkHBox" id="dialog-hbox8">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkAlignment" id="alignment7">
+            <property name="visible">True</property>
+            <property name="top_padding">5</property>
+            <property name="bottom_padding">5</property>
+            <property name="left_padding">5</property>
+            <property name="right_padding">5</property>
+            <child>
+              <object class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">5</property>
+                <child>
+                  <object class="GtkFrame" id="frame5">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment8">
+                        <property name="visible">True</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkTable" id="table1">
+                            <property name="visible">True</property>
+                            <property name="n_rows">2</property>
+                            <property name="n_columns">2</property>
+                            <child>
+                              <object class="GtkRadioButton" id="radiobutton-none">
+                                <property name="label" translatable="yes">_None</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkRadioButton" id="radiobutton-varimax">
+                                <property name="label" translatable="yes">_Varimax</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">radiobutton-none</property>
+                              </object>
+                              <packing>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkRadioButton" id="radiobutton-quartimax">
+                                <property name="label" translatable="yes">_Quartimax</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">radiobutton-none</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkRadioButton" id="radiobutton-equimax">
+                                <property name="label" translatable="yes">_Equimax</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="active">True</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">radiobutton-none</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label9">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Method</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="checkbutton-rotated-solution">
+                    <property name="label" translatable="yes">_Display rotated solution</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox11">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkLabel" id="label13">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Maximum iterations for convergence:</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="spinbutton-rot-iterations">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">&#x2022;</property>
+                        <property name="adjustment">adjustment3</property>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="PsppireVButtonBox" id="psppire-vbuttonbox3">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">5</property>
+            <property name="buttons">PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>