Patch #5672
[pspp-builds.git] / src / ui / gui / syntax-editor.c
1 /*
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 2006  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 #include <config.h>
21 #include <stdlib.h>
22 #include <gettext.h>
23 #define _(msgid) gettext (msgid)
24 #define N_(msgid) msgid
25
26 #include <glade/glade.h>
27 #include <gtk/gtk.h>
28 #include <libpspp/message.h>
29 #include <libpspp/getl.h>
30 #include "helper.h"
31 #include "data-editor.h"
32 #include "about.h"
33
34 #include "window-manager.h"
35
36 #include <data/dictionary.h>
37 #include <language/lexer/lexer.h>
38 #include <language/command.h>
39 #include <data/procedure.h>
40 #include "syntax-editor.h"
41 #include "syntax-editor-source.h"
42
43 extern struct source_stream *the_source_stream ;
44 extern struct dataset *the_dataset;
45
46 static gboolean save_editor_to_file (struct syntax_editor *se,
47                                      const gchar *filename,
48                                      GError **err);
49
50 /* Append ".sps" to FILENAME if necessary.
51    The returned result must be freed when no longer required.
52  */
53 static gchar *
54 append_suffix (const gchar *filename)
55 {
56   if ( ! g_str_has_suffix (filename, ".sps" ) &&
57        ! g_str_has_suffix (filename, ".SPS" ) )
58     {
59       return g_strdup_printf ("%s.sps", filename);
60     }
61
62   return strdup (filename);
63 }
64
65 /* If the buffer's modified flag is set, then save it, and close the window.
66    Otherwise just close the window.
67 */
68 static void
69 save_if_modified (struct syntax_editor *se)
70 {
71   struct editor_window *e = (struct editor_window *) se;
72   if ( TRUE == gtk_text_buffer_get_modified (se->buffer))
73     {
74       gint response;
75       GtkWidget *dialog =
76         gtk_message_dialog_new (GTK_WINDOW (e->window),
77                                 GTK_DIALOG_MODAL,
78                                 GTK_MESSAGE_QUESTION,
79                                 GTK_BUTTONS_NONE,
80                                 _("Save contents of syntax editor to %s?"),
81                                 e->name 
82                                 );
83
84       gtk_dialog_add_button  (GTK_DIALOG (dialog),
85                               GTK_STOCK_YES,
86                               GTK_RESPONSE_ACCEPT);
87       gtk_dialog_add_button  (GTK_DIALOG (dialog),
88                               GTK_STOCK_NO,
89                               GTK_RESPONSE_REJECT);
90       gtk_dialog_add_button  (GTK_DIALOG (dialog),
91                               GTK_STOCK_CANCEL,
92                               GTK_RESPONSE_CANCEL);
93
94
95       response = gtk_dialog_run (GTK_DIALOG (dialog));
96
97       gtk_widget_destroy (dialog);
98
99       if ( response == GTK_RESPONSE_ACCEPT )
100         {
101           GError *err = NULL;
102
103           if ( ! save_editor_to_file (se, e->name, &err) )
104             {
105               msg (ME, err->message);
106               g_error_free (err);
107             }
108         }
109
110       if ( response == GTK_RESPONSE_CANCEL )
111         return ;
112     }
113
114   gtk_widget_destroy (GTK_WIDGET (e->window));
115 }
116
117 /* Callback for the File->SaveAs menuitem */
118 static void
119 on_syntax_save_as (GtkMenuItem *menuitem, gpointer user_data)
120 {
121   GtkFileFilter *filter;
122   gint response;
123   struct syntax_editor *se = user_data;
124   struct editor_window *e = user_data;
125
126   GtkWidget *dialog =
127     gtk_file_chooser_dialog_new (_("Save Syntax"),
128                                  GTK_WINDOW (e->window),
129                                  GTK_FILE_CHOOSER_ACTION_SAVE,
130                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
131                                  GTK_STOCK_SAVE,   GTK_RESPONSE_ACCEPT,
132                                  NULL);
133
134   filter = gtk_file_filter_new ();
135   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
136   gtk_file_filter_add_pattern (filter, "*.sps");
137   gtk_file_filter_add_pattern (filter, "*.SPS");
138   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
139
140   filter = gtk_file_filter_new ();
141   gtk_file_filter_set_name (filter, _("All Files"));
142   gtk_file_filter_add_pattern (filter, "*");
143   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
144
145   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
146                                                   TRUE);
147   response = gtk_dialog_run (GTK_DIALOG (dialog));
148
149   if ( response == GTK_RESPONSE_ACCEPT )
150     {
151       GError *err = NULL;
152       char *filename =
153         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog) );
154
155       if ( save_editor_to_file (se, filename, &err) )
156         {
157           g_free (e->name);
158           e->name = g_strdup (filename);
159         }
160       else
161         {
162           msg ( ME, err->message );
163           g_error_free (err);
164         }
165
166       free (filename);
167     }
168
169   gtk_widget_destroy ( dialog );
170 }
171
172 /* Callback for the File->Save menuitem */
173 static void
174 on_syntax_save (GtkMenuItem *menuitem, gpointer user_data)
175 {
176   struct syntax_editor *se = user_data;
177   struct editor_window *e = user_data;
178
179   if ( e->name == NULL )
180     on_syntax_save_as (menuitem, user_data);
181   else
182     {
183       GError *err = NULL;
184       save_editor_to_file (se, e->name, &err);
185       if ( err )
186         {
187           msg (ME, err->message);
188           g_error_free (err);
189         }
190     }
191 }
192
193
194 /* Callback for the "delete" action (clicking the x on the top right
195    hand corner of the window) */
196 static gboolean
197 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
198 {
199   struct syntax_editor *se = user_data;
200   save_if_modified (se);
201   return TRUE;
202 }
203
204
205 /* Callback for the File->Quit menuitem */
206 static gboolean
207 on_quit (GtkMenuItem *menuitem, gpointer    user_data)
208 {
209   struct syntax_editor *se = user_data;
210   save_if_modified (se);
211   return FALSE;
212 }
213
214 static void
215 execute_syntax (const struct syntax_editor *se, GtkTextIter start,
216                 GtkTextIter stop)
217 {
218   g_return_if_fail (proc_has_source (the_dataset));
219
220   getl_append_source (the_source_stream,
221                       create_syntax_editor_source (se, start, stop));
222   for (;;)
223     {
224       const struct dictionary *dict = dataset_dict (the_dataset);
225       int result = cmd_parse (se->lexer, the_dataset,
226                               dict_get_var_cnt (dict) > 0 ?
227                               CMD_STATE_DATA : CMD_STATE_INITIAL);
228       if (result == CMD_EOF || result == CMD_FINISH)
229         break;
230     }
231
232   getl_abort_noninteractive (the_source_stream);
233 }
234
235 /* Parse and execute all the text in the buffer */
236 static void
237 on_run_all (GtkMenuItem *menuitem, gpointer user_data)
238 {
239   GtkTextIter begin, end;
240   struct syntax_editor *se = user_data;
241
242   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, 0);
243   gtk_text_buffer_get_iter_at_line (se->buffer, &end, -1);
244
245
246   execute_syntax (se, begin, end);
247 }
248
249 /* Parse and execute the currently selected text */
250 static void
251 on_run_selection (GtkMenuItem *menuitem, gpointer user_data)
252 {
253   GtkTextIter begin, end;
254   struct syntax_editor *se = user_data;
255
256   if ( gtk_text_buffer_get_selection_bounds (se->buffer, &begin, &end) )
257     execute_syntax (se, begin, end);
258 }
259
260
261 /* Parse and execute the current line */
262 static void
263 on_run_current_line (GtkMenuItem *menuitem, gpointer user_data)
264 {
265   GtkTextIter begin, end;
266   GtkTextIter here;
267   gint line;
268
269   struct syntax_editor *se = user_data;
270
271   /* Get the current line */
272   gtk_text_buffer_get_iter_at_mark (se->buffer,
273                                     &here,
274                                     gtk_text_buffer_get_insert (se->buffer)
275                                     );
276
277   line = gtk_text_iter_get_line (&here) ;
278
279   /* Now set begin and end to the start of this line, and start of
280      following line respectively */
281   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
282   gtk_text_buffer_get_iter_at_line (se->buffer, &end, line + 1);
283
284   execute_syntax (se, begin, end);
285 }
286
287
288
289 /* Parse and execute the from the current line, to the end of the
290    buffer */
291 static void
292 on_run_to_end (GtkMenuItem *menuitem, gpointer user_data)
293 {
294   GtkTextIter begin, end;
295   GtkTextIter here;
296   gint line;
297
298   struct syntax_editor *se = user_data;
299
300   /* Get the current line */
301   gtk_text_buffer_get_iter_at_mark (se->buffer,
302                                     &here,
303                                     gtk_text_buffer_get_insert (se->buffer)
304                                     );
305
306   line = gtk_text_iter_get_line (&here) ;
307
308   /* Now set begin and end to the start of this line, and end of buffer
309      respectively */
310   gtk_text_buffer_get_iter_at_line (se->buffer, &begin, line);
311   gtk_text_buffer_get_iter_at_line (se->buffer, &end, -1);
312
313   execute_syntax (se, begin, end);
314 }
315
316
317
318
319 /*
320   Create a new syntax editor with NAME.
321   If NAME is NULL, a name will be automatically assigned
322 */
323 struct syntax_editor *
324 new_syntax_editor (void)
325 {
326   GladeXML *xml =
327     glade_xml_new (PKGDATADIR "/syntax-editor.glade", NULL, NULL);
328
329   GtkWidget *text_view;
330   struct syntax_editor *se ;
331   struct editor_window *e;
332
333   connect_help (xml);
334
335   se = g_malloc (sizeof (*se));
336
337   e = (struct editor_window *)se;
338
339   e->window = GTK_WINDOW (get_widget_assert (xml, "syntax_editor"));
340   text_view = get_widget_assert (xml, "syntax_text_view");
341   se->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
342   se->lexer = lex_create (the_source_stream);
343
344   g_signal_connect (get_widget_assert (xml,"file_new_syntax"),
345                     "activate",
346                     G_CALLBACK (new_syntax_window),
347                     e->window);
348
349   g_signal_connect (get_widget_assert (xml,"file_open_syntax"),
350                     "activate",
351                     G_CALLBACK (open_syntax_window),
352                     e->window);
353
354   g_signal_connect (get_widget_assert (xml,"file_new_data"),
355                     "activate",
356                     G_CALLBACK (new_data_window),
357                     e->window);
358
359   g_signal_connect (get_widget_assert (xml,"file_open_data"),
360                     "activate",
361                     G_CALLBACK (open_data_window),
362                     e->window);
363
364
365   g_signal_connect (get_widget_assert (xml,"help_about"),
366                     "activate",
367                     G_CALLBACK (about_new),
368                     e->window);
369
370   g_signal_connect (get_widget_assert (xml,"help_reference"),
371                     "activate",
372                     G_CALLBACK (reference_manual),
373                     NULL);
374
375
376   g_signal_connect (get_widget_assert (xml, "file_save"),
377                     "activate",
378                     G_CALLBACK (on_syntax_save),
379                     se);
380
381   g_signal_connect (get_widget_assert (xml, "file_save_as"),
382                     "activate",
383                     G_CALLBACK (on_syntax_save_as),
384                     se);
385
386
387   g_signal_connect (get_widget_assert (xml,"file_quit"),
388                     "activate",
389                     G_CALLBACK (on_quit),
390                     se);
391
392
393   g_signal_connect (get_widget_assert (xml,"run_all"),
394                     "activate",
395                     G_CALLBACK (on_run_all),
396                     se);
397
398
399   g_signal_connect (get_widget_assert (xml,"run_selection"),
400                     "activate",
401                     G_CALLBACK (on_run_selection),
402                     se);
403
404   g_signal_connect (get_widget_assert (xml,"run_current_line"),
405                     "activate",
406                     G_CALLBACK (on_run_current_line),
407                     se);
408
409
410   g_signal_connect (get_widget_assert (xml,"run_to_end"),
411                     "activate",
412                     G_CALLBACK (on_run_to_end),
413                     se);
414
415
416   g_signal_connect (get_widget_assert (xml,"windows_minimise_all"),
417                     "activate",
418                     G_CALLBACK (minimise_all_windows),
419                     NULL);
420
421
422
423   g_object_unref (xml);
424
425   g_signal_connect (e->window, "delete-event",
426                     G_CALLBACK (on_delete), se);
427
428
429
430   return se;
431 }
432
433 /*
434    Callback for the File->New->Syntax menuitem
435 */
436 void
437 new_syntax_window (GtkMenuItem     *menuitem,
438                    gpointer         user_data)
439 {
440   window_create (WINDOW_SYNTAX, NULL);
441 }
442
443
444 /*
445   Save BUFFER to the file called FILENAME.
446   If successful, clears the buffer's modified flag
447 */
448 static gboolean
449 save_editor_to_file (struct syntax_editor *se,
450                      const gchar *filename,
451                      GError **err)
452 {
453   GtkTextBuffer *buffer = se->buffer;
454   gboolean result ;
455   GtkTextIter start, stop;
456   gchar *text;
457
458   gchar *suffixedname;
459   gchar *glibfilename;
460   g_assert (filename);
461
462   suffixedname = append_suffix (filename);
463
464   glibfilename = g_filename_from_utf8 (suffixedname, -1, 0, 0, err);
465
466   g_free ( suffixedname);
467
468   if ( ! glibfilename )
469     return FALSE;
470
471   gtk_text_buffer_get_iter_at_line (buffer, &start, 0);
472   gtk_text_buffer_get_iter_at_offset (buffer, &stop, -1);
473
474   text = gtk_text_buffer_get_text (buffer, &start, &stop, FALSE);
475
476   result =  g_file_set_contents (glibfilename, text, -1, err);
477
478   if ( result )
479     {
480       window_set_name_from_filename ((struct editor_window *) se, filename);
481       gtk_text_buffer_set_modified (buffer, FALSE);
482     }
483
484   return result;
485 }
486
487
488 /*
489   Loads the buffer from the file called FILENAME
490 */
491 static gboolean
492 load_editor_from_file (struct syntax_editor *se,
493                        const gchar *filename,
494                        GError **err)
495 {
496   GtkTextBuffer *buffer = se->buffer;
497   gchar *text;
498   GtkTextIter iter;
499
500   gchar *glibfilename = g_filename_from_utf8 (filename, -1, 0, 0, err);
501
502   if ( ! glibfilename )
503     return FALSE;
504
505   /* FIXME: What if it's a very big file ? */
506   if ( ! g_file_get_contents (glibfilename, &text, NULL, err) )
507     {
508       g_free (glibfilename);
509       return FALSE;
510     }
511   g_free (glibfilename);
512
513   gtk_text_buffer_get_iter_at_line (buffer, &iter, 0);
514
515   gtk_text_buffer_insert (buffer, &iter, text, -1);
516
517   window_set_name_from_filename ((struct editor_window *)se, filename);
518   gtk_text_buffer_set_modified (buffer, FALSE);
519
520   return TRUE;
521 }
522
523
524 /* Callback for the File->Open->Syntax menuitem */
525 void
526 open_syntax_window (GtkMenuItem *menuitem, gpointer parent)
527 {
528   GtkFileFilter *filter;
529   gint response;
530
531   GtkWidget *dialog =
532     gtk_file_chooser_dialog_new (_("Open Syntax"),
533                                  GTK_WINDOW (parent),
534                                  GTK_FILE_CHOOSER_ACTION_OPEN,
535                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
536                                  GTK_STOCK_OPEN,   GTK_RESPONSE_ACCEPT,
537                                  NULL);
538
539   filter = gtk_file_filter_new ();
540   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
541   gtk_file_filter_add_pattern (filter, "*.sps");
542   gtk_file_filter_add_pattern (filter, "*.SPS");
543   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
544
545   filter = gtk_file_filter_new ();
546   gtk_file_filter_set_name (filter, _("All Files"));
547   gtk_file_filter_add_pattern (filter, "*");
548   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
549
550   response = gtk_dialog_run (GTK_DIALOG (dialog));
551
552   if (response == GTK_RESPONSE_ACCEPT)
553     {
554       const char *file_name = gtk_file_chooser_get_filename
555         (GTK_FILE_CHOOSER (dialog));
556
557       struct syntax_editor *se = (struct syntax_editor *)
558         window_create (WINDOW_SYNTAX, file_name);
559
560       load_editor_from_file (se, file_name, NULL);
561     }
562
563   gtk_widget_destroy (dialog);
564 }
565