Aggregate Dialog: Convert from old fashioned method to PsppireDialogAction paradigm
[pspp] / src / ui / gui / psppire-dialog-action-aggregate.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2015  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-aggregate.h"
21
22 #include "dialog-common.h"
23
24 #include <language/stats/aggregate.h>
25
26 #include "psppire-var-view.h"
27 #include "psppire-selector.h"
28 #include "psppire-acr.h"
29 #include <stdlib.h>
30 #include "psppire-dialog.h"
31 #include "builder-wrapper.h"
32
33 #include <ui/syntax-gen.h>
34 #include <libpspp/str.h>
35
36
37 #include <gl/c-xvasprintf.h>
38
39
40 #include "gettext.h"
41 #define _(msgid) gettext (msgid)
42 #define N_(msgid) msgid
43
44
45 static void psppire_dialog_action_aggregate_init            (PsppireDialogActionAggregate      *act);
46 static void psppire_dialog_action_aggregate_class_init      (PsppireDialogActionAggregateClass *class);
47
48
49 G_DEFINE_TYPE (PsppireDialogActionAggregate, psppire_dialog_action_aggregate, PSPPIRE_TYPE_DIALOG_ACTION);
50
51 static void append_summary_spec (const PsppireDialogActionAggregate *agg, GtkTreeIter *iter, GString *string);
52
53 static void
54 append_summary_variable_syntax (const PsppireDialogActionAggregate *agg,  GString *string)
55 {
56   GtkTreeIter iter;
57   GtkTreeModel *acr_model = GTK_TREE_MODEL (PSPPIRE_ACR (agg->summary_acr)->list_store);
58
59
60   gboolean ok;
61
62   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (acr_model), &iter);
63        ok ;
64        ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (acr_model), &iter)
65        )
66     {
67       g_string_append (string, "\n\t/");
68
69       append_summary_spec (agg, &iter, string);
70     }
71 }
72
73 static void
74 append_destination_filename (const PsppireDialogActionAggregate *agg, GString *gs)
75 {
76   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (agg->filename_radiobutton)))
77     {
78       struct string ss;
79       const gchar *s = gtk_label_get_text (GTK_LABEL (agg->filename_label));
80       ds_init_empty (&ss);
81       syntax_gen_string (&ss, ss_cstr (s));
82       g_string_append (gs, ds_cstr (&ss));
83       ds_destroy (&ss);
84     }
85   else
86     {
87       g_string_append (gs, "* ");
88
89       if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (agg->replace_radiobutton)))
90         g_string_append (gs, "MODE=REPLACE");
91       else
92         g_string_append (gs, "MODE=ADDVARIABLES");
93     }
94 }
95
96
97 static char *
98 generate_syntax (PsppireDialogAction *act)
99 {
100   PsppireDialogActionAggregate *agg = PSPPIRE_DIALOG_ACTION_AGGREGATE (act);
101
102   gchar *text;
103
104   GString *string = g_string_new ("AGGREGATE OUTFILE=");
105
106   append_destination_filename (agg, string);
107
108   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (agg->sorted_button)))
109     g_string_append (string, "\n\t/PRESORTED");
110
111   g_string_append (string, "\n\t/BREAK=");
112
113   psppire_var_view_append_names (PSPPIRE_VAR_VIEW (agg->break_variables), 0, string);
114
115   append_summary_variable_syntax (agg, string);
116
117   g_string_append (string, ".\n");
118
119   text = string->str;
120
121   g_string_free (string, FALSE);
122
123   return text;
124 }
125
126
127 static gboolean
128 dialog_state_valid (gpointer user_data)
129 {
130   PsppireDialogActionAggregate *agg = user_data;
131   GtkTreeIter iter;
132   GtkTreeModel *liststore =
133     gtk_tree_view_get_model (GTK_TREE_VIEW (agg->break_variables));
134
135   if ( ! gtk_tree_model_get_iter_first  (liststore, &iter))
136     return FALSE;
137
138   liststore = GTK_TREE_MODEL (PSPPIRE_ACR (agg->summary_acr)->list_store);
139   
140   if ( ! gtk_tree_model_get_iter_first (liststore, &iter))
141     return FALSE;
142
143   return TRUE;
144 }
145
146 static void update_arguments (PsppireDialogActionAggregate *agg);
147
148
149 static void
150 refresh (PsppireDialogAction *fd_)
151 {
152   PsppireDialogActionAggregate *agg = PSPPIRE_DIALOG_ACTION_AGGREGATE (fd_);
153
154   GtkTreeModel *liststore =
155     gtk_tree_view_get_model (GTK_TREE_VIEW (agg->break_variables));
156   gtk_list_store_clear (GTK_LIST_STORE (liststore));
157
158   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (agg->add_radiobutton), TRUE);
159   gtk_label_set_text (GTK_LABEL (agg->filename_label), "");
160
161
162   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (agg->needs_sort_button), TRUE);
163
164   gtk_entry_set_text (GTK_ENTRY (agg->summary_sv_entry), "");
165   gtk_entry_set_text (GTK_ENTRY (agg->summary_arg1_entry), "");
166   gtk_entry_set_text (GTK_ENTRY (agg->summary_arg2_entry), "");
167   gtk_entry_set_text (GTK_ENTRY (agg->summary_var_label_entry), "");
168   gtk_entry_set_text (GTK_ENTRY (agg->summary_var_name_entry), "N_BREAK");
169   gtk_editable_select_region (GTK_EDITABLE (agg->summary_var_name_entry), 0, -1);
170
171   gtk_combo_box_set_active (GTK_COMBO_BOX (agg->function_combo), N);
172
173   gtk_list_store_clear (PSPPIRE_ACR (agg->summary_acr)->list_store);
174
175   update_arguments (agg);
176 }
177
178 enum
179   {
180     COMBO_MODEL_COL_DESC = 0,
181     COMBO_MODEL_COL_SYNTAX,
182     COMBO_MODEL_COL_SRC_VARS,
183     COMBO_MODEL_COL_ARITY
184   };
185
186
187
188
189 static void
190 render_summary   (GtkTreeViewColumn *tree_column,
191                   GtkCellRenderer *cell,
192                   GtkTreeModel *tree_model,
193                   GtkTreeIter *iter,
194                   gpointer data)
195 {
196  PsppireDialogActionAggregate *agg = data;
197   
198   GString *string = g_string_new ("");
199
200   append_summary_spec (agg, iter, string);
201
202   
203   g_object_set (cell, "text", string->str, NULL);
204
205   g_string_free (string, TRUE);
206 }
207
208 static void
209 choose_filename (PsppireDialogActionAggregate *fd)
210 {
211   GtkFileFilter *filter;
212
213   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Aggregate destination file"),
214                                                    GTK_WINDOW (PSPPIRE_DIALOG_ACTION (fd)->toplevel),
215                                                    GTK_FILE_CHOOSER_ACTION_SAVE,
216                                                    _("Cancel"), GTK_RESPONSE_CANCEL,
217                                                    _("Save"), GTK_RESPONSE_ACCEPT,
218                                                    NULL);
219   
220   g_object_set (dialog, "local-only", FALSE, NULL);
221
222   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
223
224
225   filter = gtk_file_filter_new ();
226   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
227   gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
228   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
229
230   filter = gtk_file_filter_new ();
231   gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
232   gtk_file_filter_add_pattern (filter, "*.zsav");
233   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
234
235   filter = gtk_file_filter_new ();
236   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
237   gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
238   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
239
240
241   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
242     {
243       char *filename;
244
245       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
246
247       gtk_label_set_text (GTK_LABEL (fd->filename_label), filename);
248
249       g_free (filename);
250     }
251
252
253   gtk_widget_destroy (dialog);
254 }
255
256
257 static void
258 populate_combo_model (GtkComboBox *cb)
259 {
260   GtkListStore *list =  gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
261   GtkTreeIter iter;
262   const struct agr_func *af = agr_func_tab;
263   GtkCellRenderer *renderer ;
264
265   for (af = agr_func_tab; af->name; ++af)
266     {
267       const gchar *s = af->description;
268       if (s == NULL)
269         continue;
270
271       gtk_list_store_append (list, &iter);
272       gtk_list_store_set (list, &iter,
273                           COMBO_MODEL_COL_DESC, gettext (s),
274                           COMBO_MODEL_COL_SYNTAX, af->name,
275                           COMBO_MODEL_COL_SRC_VARS, af->src_vars,
276                           COMBO_MODEL_COL_ARITY, af->n_args,
277                           -1);
278     }
279
280   renderer = gtk_cell_renderer_text_new ();
281   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb), renderer, FALSE);
282
283   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cb), renderer, "text", 0);
284
285   gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
286   g_object_unref (list);
287 }
288
289
290 enum 
291   {
292     SUMMARY_COL_VARNAME = 0,
293     SUMMARY_COL_VARLABEL,
294     SUMMARY_COL_FUNCIDX,
295     SUMMARY_COL_SRCVAR,
296     SUMMARY_COL_ARG1,
297     SUMMARY_COL_ARG2
298   };
299
300 /* Set VAL to the value appropriate for COL according to the
301    current state of the dialog */
302 static gboolean
303 get_summary_spec (gint col, GValue *val, gpointer data)
304 {
305   PsppireDialogActionAggregate *agg = PSPPIRE_DIALOG_ACTION_AGGREGATE (data);
306   switch (col)
307     {
308     case SUMMARY_COL_VARNAME:
309       g_value_init (val, G_TYPE_STRING);
310       g_value_set_string (val, gtk_entry_get_text (GTK_ENTRY (agg->summary_var_name_entry)));
311       break;
312     case SUMMARY_COL_VARLABEL:
313       g_value_init (val, G_TYPE_STRING);
314       g_value_set_string (val, gtk_entry_get_text (GTK_ENTRY (agg->summary_var_label_entry)));
315       break;
316     case SUMMARY_COL_SRCVAR:
317       g_value_init (val, G_TYPE_STRING);
318       g_value_set_string (val, gtk_entry_get_text (GTK_ENTRY (agg->summary_sv_entry)));
319       break;
320     case SUMMARY_COL_FUNCIDX:
321       g_value_init (val, G_TYPE_INT);
322       g_value_set_int (val, gtk_combo_box_get_active (GTK_COMBO_BOX (agg->function_combo)));
323       break;
324     case SUMMARY_COL_ARG1:
325       {
326         const gchar *text = gtk_entry_get_text (GTK_ENTRY (agg->summary_arg1_entry));
327         g_value_init (val, G_TYPE_DOUBLE);
328         g_value_set_double (val, g_strtod (text, 0));
329       }
330       break;
331     case SUMMARY_COL_ARG2:
332       {
333         const gchar *text = gtk_entry_get_text (GTK_ENTRY (agg->summary_arg2_entry));
334         g_value_init (val, G_TYPE_DOUBLE);
335         g_value_set_double (val, g_strtod (text, 0));
336       }
337       break;
338     default:
339       g_assert_not_reached ();
340       break;
341     }
342
343   return TRUE;
344 }
345
346 /* Returns TRUE iff all the necessary controls have been set to
347    completely specify a summary function */
348 static gboolean
349 summary_complete (const PsppireDialogActionAggregate *agg)
350 {
351   GtkTreeIter iter;
352   int n_args;
353   enum agr_src_vars src_vars;
354   gboolean ok;
355   GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (agg->function_combo));
356
357   if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (agg->summary_var_name_entry))))
358     return FALSE;
359
360   ok = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (agg->function_combo), &iter);
361
362   if (! ok)
363     return FALSE;
364
365
366
367   gtk_tree_model_get  (model,
368                        &iter,
369                        COMBO_MODEL_COL_ARITY,   &n_args,
370                        COMBO_MODEL_COL_SRC_VARS, &src_vars,
371                        -1);
372
373   if ( src_vars == AGR_SV_YES )
374     {
375       if (0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (agg->summary_sv_entry))))
376         return FALSE;
377     }
378
379   if ( n_args >= 2)
380     {
381       if (0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (agg->summary_arg2_entry))))
382         return FALSE;
383     }
384
385   if ( n_args >= 1)
386     {
387       if (0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (agg->summary_arg1_entry))))
388         return FALSE;
389     }
390
391
392   return TRUE;
393 }
394
395
396
397 /* Enable/Disable the summary variable ACR */
398 static void
399 update_acr (PsppireDialogActionAggregate *agg)
400 {
401   gboolean ready = summary_complete (agg);
402
403   psppire_acr_set_enabled (PSPPIRE_ACR (agg->summary_acr), ready);
404 }
405
406
407 /* Update the status of the dialog box according to what row of the ACR's treeview
408    is selected */
409 static  void
410 on_acr_change (const PsppireDialogActionAggregate *agg, GtkTreeView *tv)
411 {
412   const gchar *varname = "";
413   const gchar *label = "";
414   const gchar *srcvar = "";
415   gint f_idx = 0;
416   double arg1, arg2;
417   gchar *text1 = g_strdup ("");
418   gchar *text2 = g_strdup ("");
419     
420   GtkTreeIter iter;
421   GtkTreeModel *model = gtk_tree_view_get_model (tv);
422   GtkTreeSelection *sel = gtk_tree_view_get_selection (tv);
423
424   if (gtk_tree_selection_get_selected (sel, &model, &iter))
425     {
426       gtk_tree_model_get (model, &iter,
427                           SUMMARY_COL_VARNAME, &varname,
428                           SUMMARY_COL_VARLABEL, &label,
429                           SUMMARY_COL_FUNCIDX, &f_idx,
430                           SUMMARY_COL_SRCVAR, &srcvar,
431                           SUMMARY_COL_ARG1, &arg1,
432                           SUMMARY_COL_ARG2, &arg2, -1);
433
434       gtk_entry_set_text (GTK_ENTRY (agg->summary_var_name_entry), varname);
435       gtk_entry_set_text (GTK_ENTRY (agg->summary_var_label_entry), label);
436       gtk_entry_set_text (GTK_ENTRY (agg->summary_sv_entry), srcvar);
437   
438       text1 = c_xasprintf ("%.*g", DBL_DIG + 1, arg1);
439       text2 = c_xasprintf ("%.*g", DBL_DIG + 1, arg2);
440     }
441
442   gtk_entry_set_text (GTK_ENTRY (agg->summary_arg1_entry), text1);
443   g_free (text1);
444
445   gtk_entry_set_text (GTK_ENTRY (agg->summary_arg2_entry), text2);
446   g_free (text2);
447
448   gtk_combo_box_set_active (GTK_COMBO_BOX (agg->function_combo), f_idx);
449 }
450
451
452 /* Update the sensitivity of the summary variable argument fields */
453 static void
454 update_arguments (PsppireDialogActionAggregate *agg)
455 {
456   GtkTreeIter iter;
457
458   gboolean ok = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (agg->function_combo), &iter);
459
460   if ( ok)
461     {
462       GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (agg->function_combo));
463       int n_args;
464       enum agr_src_vars src_vars;
465       gtk_tree_model_get  (model,
466                            &iter,
467                            COMBO_MODEL_COL_ARITY,   &n_args,
468                            COMBO_MODEL_COL_SRC_VARS, &src_vars,
469                            -1);
470
471       gtk_widget_set_sensitive (agg->summary_sv, src_vars != AGR_SV_NO);
472       gtk_widget_set_sensitive (agg->summary_arg2, n_args >= 2);
473       gtk_widget_set_sensitive (agg->summary_arg1, n_args >= 1);
474     }
475   else
476     {
477       gtk_widget_set_sensitive (agg->summary_sv,   FALSE);
478       gtk_widget_set_sensitive (agg->summary_arg2, FALSE);
479       gtk_widget_set_sensitive (agg->summary_arg1, FALSE);
480     }
481 }
482
483
484
485 static void
486 psppire_dialog_action_aggregate_activate (GtkAction *a)
487 {
488   PsppireDialogActionAggregate *act = PSPPIRE_DIALOG_ACTION_AGGREGATE (a);
489   PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
490
491   GHashTable *thing = psppire_dialog_action_get_hash_table (pda);
492   GtkBuilder *xml = g_hash_table_lookup (thing, a);
493   if (!xml)
494     {
495       xml = builder_new ("aggregate.ui");
496       g_hash_table_insert (thing, a, xml);
497
498
499       pda->dialog = get_widget_assert (xml, "aggregate-dialog");
500       pda->source = get_widget_assert (xml, "dict-view");
501
502
503
504       GtkWidget *break_selector = get_widget_assert   (xml, "break-selector");
505
506       act->pane = get_widget_assert (xml, "hbox1");
507   
508       act->break_variables = get_widget_assert (xml, "psppire-var-view1");
509       act->filename_radiobutton = get_widget_assert (xml, "filename-radiobutton");
510       act->filename_button = get_widget_assert (xml, "filename-button");
511       act->filename_box = get_widget_assert (xml, "filename-box");
512       act->filename_label = get_widget_assert (xml, "filename-label");
513       act->replace_radiobutton = get_widget_assert (xml, "replace-radiobutton");
514       act->add_radiobutton = get_widget_assert (xml, "add-radiobutton");
515       act->function_combo = get_widget_assert (xml, "function-combo");
516
517       act->summary_acr = get_widget_assert (xml, "psppire-acr1");
518       act->summary_var_name_entry = get_widget_assert (xml, "summary-var-name-entry");
519
520       act->summary_arg1 = get_widget_assert (xml, "summary-arg1");
521       act->summary_arg2 = get_widget_assert (xml, "summary-arg2");
522
523       act->summary_arg1_entry = get_widget_assert (xml, "summary-arg-entry1");
524       act->summary_arg2_entry = get_widget_assert (xml, "summary-arg-entry2");
525
526       act->summary_var_label_entry = get_widget_assert (xml, "summary-var-label-entry");
527
528       act->summary_sv = get_widget_assert (xml, "source-var");
529       act->summary_sv_entry = get_widget_assert (xml, "source-var-entry");
530
531       act->sorted_button = get_widget_assert (xml, "sorted-radiobutton");
532       act->needs_sort_button = get_widget_assert (xml, "needs-sort-radiobutton");
533
534       {
535         GtkTreeViewColumn *column ;
536
537         GList *l ;
538
539         GtkCellRenderer *cell_renderer ;
540
541         GtkListStore *list = gtk_list_store_new (6,
542                                                  G_TYPE_STRING,
543                                                  G_TYPE_STRING,
544                                                  G_TYPE_INT, 
545                                                  G_TYPE_STRING,
546                                                  G_TYPE_DOUBLE,
547                                                  G_TYPE_DOUBLE);
548
549         psppire_acr_set_model (PSPPIRE_ACR (act->summary_acr), list);
550         g_object_unref (list);
551
552         psppire_acr_set_get_value_func (PSPPIRE_ACR (act->summary_acr),
553                                         get_summary_spec, act);
554
555         column = gtk_tree_view_get_column (PSPPIRE_ACR (act->summary_acr)->tv, 0);
556
557         l = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
558
559         cell_renderer = l->data;
560
561         gtk_tree_view_column_set_cell_data_func (column,
562                                                  cell_renderer,
563                                                  render_summary,
564                                                  act,
565                                                  NULL);
566
567         g_signal_connect_swapped (PSPPIRE_ACR (act->summary_acr)->tv,
568                                   "cursor-changed", G_CALLBACK (on_acr_change), act);
569       }
570   
571       g_signal_connect_swapped (act->summary_var_name_entry, "changed", G_CALLBACK (update_acr),  act);
572       g_signal_connect_swapped (act->function_combo, "changed", G_CALLBACK (update_acr),  act);
573       g_signal_connect_swapped (act->summary_sv_entry, "changed", G_CALLBACK (update_acr),  act);
574       g_signal_connect_swapped (act->summary_arg1_entry, "changed", G_CALLBACK (update_acr),  act);
575       g_signal_connect_swapped (act->summary_arg2_entry, "changed", G_CALLBACK (update_acr),  act);
576
577
578       g_signal_connect_swapped (act->function_combo, "changed",
579                                 G_CALLBACK (update_arguments),  act);
580
581       populate_combo_model (GTK_COMBO_BOX (act->function_combo));
582
583
584       psppire_selector_set_filter_func (PSPPIRE_SELECTOR (break_selector), NULL);
585
586
587       g_signal_connect (act->filename_radiobutton, "toggled",
588                         G_CALLBACK (set_sensitivity_from_toggle), act->filename_box );
589
590       g_signal_connect_swapped (act->filename_button, "clicked",
591                                 G_CALLBACK (choose_filename), act);
592
593       psppire_dialog_action_set_refresh (pda, refresh);
594       psppire_dialog_action_set_valid_predicate (pda, dialog_state_valid);
595     }
596
597   if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_aggregate_parent_class)->activate)
598     PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_aggregate_parent_class)->activate (pda);
599 }
600
601 static void
602 psppire_dialog_action_aggregate_class_init (PsppireDialogActionAggregateClass *class)
603 {
604   psppire_dialog_action_set_activation (class, psppire_dialog_action_aggregate_activate);
605   PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax;
606 }
607
608
609 static void
610 psppire_dialog_action_aggregate_init (PsppireDialogActionAggregate *act)
611 {
612 }
613
614
615 /* Append the syntax of the summary function pointed to by ITER to STRING */
616 static void
617 append_summary_spec (const PsppireDialogActionAggregate *agg, GtkTreeIter *iter, GString *string)
618 {
619   GtkTreeIter combo_iter;
620   char *varname = NULL;
621   char *funcname = NULL;
622
623   GtkTreeModel *acr_model = GTK_TREE_MODEL (PSPPIRE_ACR (agg->summary_acr)->list_store);
624   GtkTreeModel *combo_model = gtk_combo_box_get_model (GTK_COMBO_BOX (agg->function_combo));
625
626
627   /* This is an index into the combo_model.  Its used to get the function name */
628   int f_idx;
629   double arg1, arg2;
630   int arity;
631   enum agr_src_vars has_src_vars;
632   gchar *label = NULL;
633   gchar *srcvar = NULL;
634
635   gtk_tree_model_get (acr_model, iter,
636                       SUMMARY_COL_VARNAME, &varname,
637                       SUMMARY_COL_VARLABEL, &label,
638                       SUMMARY_COL_FUNCIDX, &f_idx,
639                       SUMMARY_COL_SRCVAR, &srcvar,
640                       SUMMARY_COL_ARG1, &arg1,
641                       SUMMARY_COL_ARG2, &arg2,
642                       -1);
643
644   gtk_tree_model_iter_nth_child (combo_model, &combo_iter, NULL, f_idx);
645
646   gtk_tree_model_get (combo_model, &combo_iter,
647                       COMBO_MODEL_COL_SYNTAX, &funcname,
648                       COMBO_MODEL_COL_ARITY, &arity,
649                       COMBO_MODEL_COL_SRC_VARS, &has_src_vars,
650                       -1);
651
652   g_string_append (string, varname);
653
654   if (0 != strcmp ("", label))
655     {
656       struct string ss;
657       ds_init_empty (&ss);
658       syntax_gen_string (&ss, ss_cstr (label));
659       g_string_append (string, " ");
660       g_string_append (string, ds_cstr (&ss));
661       ds_destroy (&ss);
662     }
663     
664   g_string_append_printf (string, " = %s", funcname);
665
666   if ( has_src_vars != AGR_SV_NO)
667     {
668       struct string dss;
669       ds_init_cstr (&dss, " (");
670       
671       ds_put_cstr (&dss, srcvar);
672
673       if ( arity > 0)
674         ds_put_c_format (&dss, ", %.*g", DBL_DIG + 1, arg1);
675
676       if ( arity > 1)
677         ds_put_c_format (&dss, ", %.*g", DBL_DIG + 1, arg2);
678
679       ds_put_cstr (&dss, ")");
680
681       g_string_append (string, ds_cstr (&dss));
682
683       ds_destroy (&dss);
684     }
685
686    free (label);
687    free (srcvar);
688    free (varname);
689    free (funcname);
690 }