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