New module matrix-reader
[pspp] / src / ui / gui / executor.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009, 2010, 2011, 2012  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 "ui/gui/executor.h"
20
21 #include "data/dataset.h"
22 #include "data/lazy-casereader.h"
23 #include "data/session.h"
24 #include "language/command.h"
25 #include "language/lexer/lexer.h"
26 #include "libpspp/cast.h"
27 #include "output/driver.h"
28 #include "ui/gui/psppire-data-store.h"
29 #include "ui/gui/psppire.h"
30
31 /* Lazy casereader callback function used by execute_syntax. */
32 static struct casereader *
33 create_casereader_from_data_store (void *data_store_)
34 {
35   PsppireDataStore *data_store = data_store_;
36   return psppire_data_store_get_reader (data_store);
37 }
38
39 /* Ensures that dataset DS has a name, because some parts of the GUI insist
40    upon this. */
41 static void
42 name_dataset_cb (struct dataset *ds, void *aux UNUSED)
43 {
44   if (dataset_name (ds)[0] == '\0')
45     {
46       struct session *session = dataset_session (ds);
47       char *dataset_name = session_generate_dataset_name (session);
48       dataset_set_name (ds, dataset_name);
49       free (dataset_name);
50     }
51 }
52
53 static void
54 new_pdw_cb (struct dataset *ds, void *aux UNUSED)
55 {
56   PsppireDataWindow *pdw = psppire_data_window_for_dataset (ds);
57   if (pdw == NULL)
58     pdw = PSPPIRE_DATA_WINDOW (psppire_data_window_new (ds));
59
60   switch (dataset_get_display (ds))
61     {
62     case DATASET_ASIS:
63       break;
64
65     case DATASET_FRONT:
66       gtk_widget_show (GTK_WIDGET (pdw));
67       gtk_window_deiconify (GTK_WINDOW (pdw));
68       gdk_window_raise (gtk_widget_get_window (GTK_WIDGET (pdw)));
69       psppire_data_window_set_default (pdw);
70       break;
71
72     case DATASET_MINIMIZED:
73       gtk_window_iconify (GTK_WINDOW (pdw));
74       gtk_widget_show (GTK_WIDGET (pdw));
75       psppire_data_window_undefault (pdw);
76       break;
77
78     case DATASET_HIDDEN:
79       gtk_widget_hide (GTK_WIDGET (pdw));
80       psppire_data_window_undefault (pdw);
81       break;
82     }
83   dataset_set_display (ds, DATASET_ASIS);
84 }
85
86 gboolean
87 execute_syntax (PsppireDataWindow *window, struct lex_reader *lex_reader)
88 {
89   struct lexer *lexer;
90   gboolean retval = TRUE;
91
92   PsppireDataWindow *pdw, *next_pdw;
93
94   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
95     {
96       const struct caseproto *proto;
97       struct casereader *reader;
98       casenumber case_cnt;
99
100       /* When the user executes a number of snippets of syntax in a
101          row, none of which read from the active dataset, the GUI becomes
102          progressively less responsive.  The reason is that each syntax
103          execution encapsulates the active dataset data in another
104          datasheet layer.  The cumulative effect of having a number of
105          layers of datasheets wastes time and space.
106
107          To solve the problem, we use a "lazy casereader", a wrapper
108          around the casereader obtained from the data store, that
109          only actually instantiates that casereader when it is
110          needed.  If the data store casereader is never needed, then
111          it is reused the next time syntax is run, without wrapping
112          it in another layer. */
113       proto = psppire_data_store_get_proto (pdw->data_store);
114       case_cnt = psppire_data_store_get_case_count (pdw->data_store);
115       reader = lazy_casereader_create (proto, case_cnt,
116                                        create_casereader_from_data_store,
117                                        pdw->data_store, &pdw->lazy_serial);
118       dataset_set_source (pdw->dataset, reader);
119
120       if (pdw == window)
121         session_set_active_dataset (the_session, pdw->dataset);
122
123       g_return_val_if_fail (dataset_has_source (pdw->dataset), FALSE);
124
125       pdw->dataset_seqno = dataset_seqno (pdw->dataset);
126     }
127
128   lexer = lex_create ();
129   psppire_set_lexer (lexer);
130   lex_append (lexer, lex_reader);
131
132   for (;;)
133     {
134       struct dataset *ds = session_active_dataset (the_session);
135       enum cmd_result result = cmd_parse (lexer, ds);
136
137       if ( cmd_result_is_failure (result))
138         {
139           retval = FALSE;
140           if ( lex_get_error_mode (lexer) == LEX_ERROR_STOP )
141             break;
142         }
143
144       if ( result == CMD_EOF || result == CMD_FINISH)
145         break;
146     }
147
148   session_for_each_dataset (the_session, name_dataset_cb, NULL);
149
150   ll_for_each_safe (pdw, next_pdw, PsppireDataWindow, ll, &all_data_windows)
151     {
152       struct dataset *ds;
153
154       ds = session_get_dataset_by_seqno (the_session, pdw->dataset_seqno);
155       if (ds != NULL)
156         {
157           struct casereader *reader;
158
159           pdw->dataset = ds;
160           proc_execute (pdw->dataset);
161
162           psppire_dict_replace_dictionary (pdw->data_store->dict,
163                                            dataset_dict (pdw->dataset));
164
165           reader = dataset_steal_source (pdw->dataset);
166           if (!lazy_casereader_destroy (reader, pdw->lazy_serial))
167             psppire_data_store_set_reader (pdw->data_store, reader);
168
169           g_object_set (G_OBJECT (pdw), "id", dataset_name (pdw->dataset),
170                         (void *) NULL);
171         }
172       else
173         gtk_widget_destroy (GTK_WIDGET (pdw));
174     }
175
176   session_for_each_dataset (the_session, new_pdw_cb, NULL);
177
178   /* Destroy the lexer only after obtaining the dataset, because the dataset
179      might depend on the lexer, if the casereader specifies inline data.  (In
180      such a case then we'll always get an error message--the inline data is
181      missing, otherwise it would have been parsed in the loop above.) */
182   lex_destroy (lexer);
183   psppire_set_lexer (NULL);
184
185   output_flush ();
186
187   return retval;
188 }
189
190 /* Executes null-terminated string SYNTAX as syntax.
191    Returns SYNTAX. */
192 gchar *
193 execute_syntax_string (PsppireDataWindow *window, gchar *syntax)
194 {
195   execute_const_syntax_string (window, syntax);
196   return syntax;
197 }
198
199 /* Executes null-terminated string SYNTAX as syntax. */
200 void
201 execute_const_syntax_string (PsppireDataWindow *window, const gchar *syntax)
202 {
203   execute_syntax (window, lex_reader_for_string (syntax, "UTF-8"));
204 }