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