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