Added the Compute dialog box.
[pspp-builds.git] / src / ui / gui / compute-dialog.c
1 /*
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 2007  Free Software Foundation
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18     02110-1301, USA. */
19
20
21 #include <gtk/gtk.h>
22 #include "compute-dialog.h"
23 #include "helper.h"
24 #include "psppire-dialog.h"
25 #include "psppire-keypad.h"
26 #include "data-editor.h"
27 #include <gtksheet/gtksheet.h>
28 #include "psppire-var-store.h"
29 #include "dialog-common.h"
30 #include "dict-display.h"
31
32 #include <language/syntax-string-source.h>
33 #include "syntax-editor.h"
34
35
36 #include <language/expressions/private.h>
37 #include "c-ctype.h"
38
39 static void function_list_populate (GtkTreeView *tv);
40
41 static void insert_function_into_syntax_area (GtkTreeIter iter,
42                                               GtkWidget *text_view,
43                                               GtkTreeModel *model
44                                               );
45
46 static void insert_source_row_into_text_view (GtkTreeIter iter,
47                                               GtkWidget *dest,
48                                               GtkTreeModel *model
49                                               );
50
51
52
53 struct compute_dialog
54 {
55   GladeXML *xml;  /* The xml that generated the widgets */
56 };
57
58
59 static void
60 on_target_change (GObject *obj, const struct compute_dialog *cd)
61 {
62   GtkWidget *target = get_widget_assert (cd->xml, "compute-entry1");
63   GtkWidget *type_and_label = get_widget_assert (cd->xml, "compute-button1");
64
65   const gchar *var_name = gtk_entry_get_text (GTK_ENTRY (target));
66   gboolean valid = var_name && strcmp ("", var_name);
67
68   gtk_widget_set_sensitive (type_and_label, valid);
69 }
70
71 static void
72 refresh (GObject *obj, const struct compute_dialog *cd)
73 {
74   GtkTextIter start, end;
75   GtkWidget *target = get_widget_assert (cd->xml, "compute-entry1");
76   GtkWidget *syntax_area = get_widget_assert (cd->xml, "compute-textview1");
77   GtkWidget *varlist = get_widget_assert (cd->xml, "compute-treeview1");
78   GtkWidget *funclist = get_widget_assert (cd->xml, "compute-treeview2");
79
80   GtkTextBuffer *buffer =
81     gtk_text_view_get_buffer (GTK_TEXT_VIEW (syntax_area));
82
83   GtkTreeSelection *selection;
84
85
86   /* Clear the target variable entry box */
87   gtk_entry_set_text (GTK_ENTRY (target), "");
88   g_signal_emit_by_name (target, "changed");
89
90
91   /* Clear the syntax area textbuffer */
92   gtk_text_buffer_get_start_iter (buffer, &start);
93   gtk_text_buffer_get_end_iter (buffer, &end);
94   gtk_text_buffer_delete (buffer, &start, &end);
95
96
97   /* Unselect all items in the treeview */
98   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (varlist));
99   gtk_tree_selection_unselect_all (selection);
100
101   /* And the other one */
102   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (funclist));
103   gtk_tree_selection_unselect_all (selection);
104 }
105
106
107 static void
108 erase_selection (GtkTextBuffer *buffer)
109 {
110   GtkTextIter start, end;
111
112   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
113
114   gtk_text_buffer_delete (buffer, &start, &end);
115 }
116
117
118
119 static void
120 on_keypad_button (PsppireKeypad *kp, const gchar *syntax, gpointer data)
121 {
122   GladeXML *xml = data;
123
124   GtkWidget *rhs = get_widget_assert (xml, "compute-textview1");
125
126   GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (rhs));
127
128   erase_selection (buffer);
129
130   gtk_text_buffer_insert_at_cursor (buffer, syntax, strlen (syntax));
131
132   if (0 == strcmp (syntax, "()"))
133     {
134       GtkTextIter iter;
135       GtkTextMark *cursor = gtk_text_buffer_get_insert (buffer);
136       gtk_text_buffer_get_iter_at_mark (buffer, &iter, cursor);
137       gtk_text_iter_backward_cursor_position (&iter);
138       gtk_text_buffer_move_mark (buffer, cursor, &iter);
139     }
140
141 }
142
143 static void
144 erase (PsppireKeypad *kp, gpointer data)
145 {
146   GladeXML *xml = data;
147
148   GtkWidget *rhs = get_widget_assert (xml, "compute-textview1");
149
150   GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (rhs));
151
152   erase_selection (buffer);
153 }
154
155 static char *
156 generate_syntax (const struct compute_dialog *cd)
157 {
158   gchar *text;
159   GString *string ;
160   GtkTextIter start, end;
161   GtkWidget *target =    get_widget_assert   (cd->xml, "compute-entry1");
162   GtkWidget *syntax_area = get_widget_assert (cd->xml, "compute-textview1");
163
164   GtkTextBuffer *buffer =
165     gtk_text_view_get_buffer (GTK_TEXT_VIEW (syntax_area));
166
167   gtk_text_buffer_get_start_iter (buffer, &start);
168   gtk_text_buffer_get_end_iter (buffer, &end);
169
170
171   string = g_string_new ("COMPUTE ");
172
173   g_string_append (string, gtk_entry_get_text (GTK_ENTRY (target)));
174
175   g_string_append (string, " = ");
176
177   g_string_append (string,
178                    gtk_text_buffer_get_text (buffer, &start, &end, FALSE));
179
180   g_string_append (string, ".");
181
182   text = string->str;
183
184   g_string_free (string, FALSE);
185
186   return text;
187 }
188
189
190 /* Pops up the Compute dialog box */
191 void
192 compute_dialog (GObject *o, gpointer data)
193 {
194   gint response;
195   struct data_editor *de = data;
196
197   PsppireVarStore *vs;
198   struct compute_dialog scd;
199
200   GladeXML *xml = XML_NEW ("psppire.glade");
201
202   GtkWidget *dialog = get_widget_assert   (xml, "compute-variable-dialog");
203
204   GtkWidget *dict_view = get_widget_assert   (xml, "compute-treeview1");
205   GtkWidget *functions = get_widget_assert   (xml, "compute-treeview2");
206   GtkWidget *keypad =    get_widget_assert   (xml, "psppire-keypad1");
207   GtkWidget *target =    get_widget_assert   (xml, "compute-entry1");
208   GtkWidget *syntax_area = get_widget_assert (xml, "compute-textview1");
209   GtkWidget *var_selector = get_widget_assert (xml, "compute-selector1");
210   GtkWidget *func_selector = get_widget_assert (xml, "compute-selector2");
211
212   GtkSheet *var_sheet =
213     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
214
215   vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
216
217
218   gtk_window_set_transient_for (GTK_WINDOW (dialog), de->parent.window);
219
220
221   attach_dictionary_to_treeview (GTK_TREE_VIEW (dict_view),
222                                  vs->dict,
223                                  GTK_SELECTION_SINGLE, NULL);
224
225
226   psppire_selector_set_subjects (PSPPIRE_SELECTOR (var_selector),
227                                  dict_view, syntax_area,
228                                  insert_source_row_into_text_view,
229                                  NULL);
230
231
232   function_list_populate (GTK_TREE_VIEW (functions));
233
234   psppire_selector_set_subjects (PSPPIRE_SELECTOR (func_selector),
235                                  functions, syntax_area,
236                                  insert_function_into_syntax_area,
237                                  NULL);
238
239
240   scd.xml = xml;
241
242   g_signal_connect (target, "changed", G_CALLBACK (on_target_change), &scd);
243
244   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &scd);
245
246   g_signal_connect (keypad, "insert-syntax",
247                     G_CALLBACK (on_keypad_button),  xml);
248
249   g_signal_connect (keypad, "erase",
250                     G_CALLBACK (erase),  xml);
251
252
253   response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
254
255
256   switch (response)
257     {
258     case GTK_RESPONSE_OK:
259       {
260         gchar *syntax = generate_syntax (&scd);
261         struct getl_interface *sss = create_syntax_string_source (syntax);
262         execute_syntax (sss);
263
264         g_free (syntax);
265       }
266       break;
267     case PSPPIRE_RESPONSE_PASTE:
268       {
269         gchar *syntax = generate_syntax (&scd);
270
271         struct syntax_editor *se =
272           (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
273
274         gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1);
275
276         g_free (syntax);
277       }
278       break;
279     default:
280       break;
281     }
282
283   g_object_unref (xml);
284 }
285
286
287 enum {
288   COL_NAME,
289   COL_USAGE,
290   COL_ARITY
291 };
292
293
294 static const struct operation fs[] =
295   {
296 #include <language/expressions/parse.inc>
297   };
298
299
300 static void
301 function_list_populate (GtkTreeView *tv)
302 {
303   GtkListStore *liststore;
304   GtkTreeIter iter;
305   gint i;
306
307   const gint n_funcs = sizeof (fs) / sizeof fs[0] ;
308
309   liststore = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
310
311   for (i = 0 ; i < n_funcs ; ++i)
312     {
313       if ( fs[i].prototype == NULL)
314         continue;
315
316       /* All the real ones seem to begin with an upper case letter */
317       if ( !c_isupper(*fs[i].prototype))
318         continue;
319
320       gtk_list_store_append (liststore, &iter);
321
322       gtk_list_store_set (liststore, &iter,
323                           COL_NAME, fs[i].name,
324                           COL_USAGE,fs[i].prototype,
325                           COL_ARITY, fs[i].arg_cnt,
326                           -1);
327     }
328
329
330
331   /* Set the cell rendering */
332
333   {
334     GtkTreeViewColumn *col;
335     GtkCellRenderer *renderer;
336
337
338     col = gtk_tree_view_column_new ();
339
340     gtk_tree_view_append_column (tv, col);
341
342     renderer = gtk_cell_renderer_text_new ();
343
344     gtk_tree_view_column_pack_start (col, renderer, TRUE);
345
346     gtk_tree_view_column_add_attribute (col, renderer, "text", COL_USAGE);
347   }
348
349   gtk_tree_view_set_model (tv, GTK_TREE_MODEL (liststore));
350 }
351
352
353
354
355 static void
356 insert_function_into_syntax_area (GtkTreeIter iter,
357                                   GtkWidget *text_view,
358                                   GtkTreeModel *model
359                                   )
360 {
361   GString *string;
362   GValue name_value = {0};
363   GValue arity_value = {0};
364   gint arity;
365   gint i;
366
367   GtkTextBuffer *buffer ;
368
369   g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
370
371   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
372
373   gtk_tree_model_get_value (model, &iter, COL_NAME, &name_value);
374   gtk_tree_model_get_value (model, &iter, COL_ARITY, &arity_value);
375
376   arity = g_value_get_int (&arity_value);
377
378   string = g_string_new (g_value_get_string (&name_value));
379
380   g_string_append (string, "(");
381   for ( i = 0 ; i < arity -1 ; ++i )
382     {
383       g_string_append (string, "?,");
384     }
385   g_string_append (string, "?)");
386
387
388   erase_selection (buffer);
389
390   gtk_text_buffer_insert_at_cursor (buffer, string->str, string->len);
391
392   g_value_unset (&name_value);
393   g_value_unset (&arity_value);
394   g_string_free (string, TRUE);
395
396   /* Now position the cursor over the first '?' */
397   {
398     GtkTextIter insert;
399     GtkTextIter selectbound;
400     GtkTextMark *cursor = gtk_text_buffer_get_insert (buffer);
401     gtk_text_buffer_get_iter_at_mark (buffer, &insert, cursor);
402     for ( i = 0 ; i < arity ; ++i )
403       {
404         gtk_text_iter_backward_cursor_position (&insert);
405         gtk_text_iter_backward_cursor_position (&insert);
406       }
407     selectbound = insert;
408     gtk_text_iter_forward_cursor_position (&selectbound);
409
410     gtk_text_buffer_select_range (buffer, &insert, &selectbound);
411   }
412
413 }
414
415 /* Inserts the name of the selected variable into the destination widget.
416    The destination widget must be a GtkTextView
417  */
418 static void
419 insert_source_row_into_text_view (GtkTreeIter iter,
420                                   GtkWidget *dest,
421                                   GtkTreeModel *model
422                                   )
423 {
424   GtkTreePath *path;
425   PsppireDict *dict;
426   gint *idx;
427   struct variable *var;
428   GtkTreeIter dict_iter;
429   gchar *name;
430   GtkTextBuffer *buffer;
431
432   g_return_if_fail (GTK_IS_TEXT_VIEW (dest));
433
434   if ( GTK_IS_TREE_MODEL_FILTER (model))
435     {
436       dict = PSPPIRE_DICT (gtk_tree_model_filter_get_model
437                            (GTK_TREE_MODEL_FILTER(model)));
438
439       gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER
440                                                         (model),
441                                                         &dict_iter, &iter);
442     }
443   else
444     {
445       dict = PSPPIRE_DICT (model);
446       dict_iter = iter;
447     }
448
449   path = gtk_tree_model_get_path (GTK_TREE_MODEL (dict), &dict_iter);
450
451   idx = gtk_tree_path_get_indices (path);
452
453   var =  psppire_dict_get_variable (dict, *idx);
454
455   gtk_tree_path_free (path);
456
457   name = pspp_locale_to_utf8 (var_get_name (var), -1, NULL);
458
459   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dest));
460
461   erase_selection (buffer);
462
463   gtk_text_buffer_insert_at_cursor (buffer, name, -1);
464
465   g_free (name);
466 }